home *** CD-ROM | disk | FTP | other *** search
/ TeX 1995 July / TeX CD-ROM July 1995 (Disc 1)(Walnut Creek)(1995).ISO / biblio / bibtex / utils / bibclean / bibclean.c < prev    next >
Text File  |  1992-11-23  |  148KB  |  5,639 lines

  1. /***********************************************************************
  2.   @C-file{
  3.     author              = "Nelson H. F. Beebe",
  4.     version             = "2.05",
  5.     date                = "24 November 1992",
  6.     time                = "10:14:50 MST",
  7.     filename            = "bibclean.c",
  8.     address             = "Center for Scientific Computing
  9.                            Department of Mathematics
  10.                            University of Utah
  11.                            Salt Lake City, UT 84112
  12.                            USA",
  13.     telephone           = "+1 801 581 5254",
  14.     FAX                 = "+1 801 581 4148",
  15.     checksum            = "65270 5638 19219 147882",
  16.     email               = "beebe@math.utah.edu (Internet)",
  17.     codetable           = "ISO/ASCII",
  18.     keywords            = "prettyprint, bibliography",
  19.     supported           = "yes",
  20.     docstring           = {Prettyprint one or more BibTeX files on stdin,
  21.                            or specified files, to stdout, and check
  22.                            the brace balance and value strings as well.
  23.  
  24.                            Text outside @item-type{...} BibTeX entries
  25.                            is passed through verbatim, except that
  26.                            trailing blanks are trimmed.
  27.  
  28.                            BibTeX items are formatted into a consistent
  29.                            structure with one key = "value" pair per
  30.                            line, and the initial @ and trailing right
  31.                            brace in column 1.  Long values are split at a
  32.                            blank and continued onto the next line with
  33.                            leading indentation.  Tabs are expanded into
  34.                            blank strings; their use is discouraged
  35.                            because they inhibit portability, and can
  36.                            suffer corruption in electronic mail.  Braced
  37.                            strings are converted to quoted strings.
  38.  
  39.                            This format facilitates the later application
  40.                            of simple filters to process the text for
  41.                            extraction of items, and also is the one
  42.                            expected by the GNU Emacs BibTeX support
  43.                            functions.
  44.  
  45.                            Usage:
  46.  
  47.                            bibclean [ -author ] [ -error-log filename ]
  48.                                     [ -help ] [ '-?' ] [ -init-file filename ]
  49.                                     [ -[no-]check-values ]
  50.                                     [ -[no-]delete-empty-fields ]
  51.                                     [ -[no-]file-position ]
  52.                                     [ -[no-]fix-initials ] [ -[no-]fix-names ]
  53.                                     [ -[no-]par-breaks ]
  54.                                     [ -[no-]print-patterns ]
  55.                                     [ -[no-]read-init-files ]
  56.                                     [ -[no-]remove-OPT-prefixes ]
  57.                                     [ -[no-]scribe ]
  58.                                     [ -[no-]trace-file-opening ]
  59.                                     [ -[no-]warnings ] [ -version ]
  60.                                     <infile or  bibfile1 bibfile2 bibfile3 ...
  61.                                     >outfile
  62.  
  63.                            The checksum field above contains a CRC-16
  64.                            checksum as the first value, followed by the
  65.                            equivalent of the standard UNIX wc (word
  66.                            count) utility output of lines, words, and
  67.                            characters.  This is produced by Robert
  68.                            Solovay's checksum utility.},
  69.        }
  70. ***********************************************************************/
  71.  
  72. /***********************************************************************
  73.  
  74. The formatting should perhaps be user-customizable; that is left for
  75. future work.
  76.  
  77. The major goal has been to convert entries to the standard form
  78.  
  79. @item-type{citation-key,
  80.   key =          "value",
  81.   key =          "value",
  82.   ...
  83. }
  84.  
  85. while applying heuristics to permit early error detection.  If
  86. the input file is syntactically correct for BibTeX and LaTeX,
  87. this is reasonably easy.  If the file has errors, error recovery
  88. is attempted, but cannot be guaranteed to be successful; however,
  89. the output file, and stderr, will contain an error message that
  90. should localize the error to a single entry where a human can
  91. find it more easily than a computer can.  To facilitate error
  92. checking and recovery, the following conditions are used:
  93.  
  94.     @    starts a BibTeX entry only if it occurs at brace
  95.         level 0 and is not preceded by non-blank text on
  96.         the same line.
  97.     "    is significant only at brace level 1.
  98.     {}    are expected to occur at @-level 1 or higher
  99.     }    at beginning of line ends a BibTeX entry
  100.  
  101. Backslashes preceding these 4 characters remove their special
  102. significance.
  103.  
  104. These heuristics are needed to deal with legal value strings like
  105.  
  106.     {..."...}
  107.     "...{..}..."
  108.  
  109. and will flag as errors strings like
  110.  
  111.     "...{..."
  112.     "...}..."
  113.  
  114. The special treatment of @ and } at beginning of line attempts to
  115. detect errors in entries before the rest of the file is swallowed
  116. up in an attempt to complete an unclosed entry.
  117.  
  118. The output bibliography file should be processed by BibTeX and
  119. the LaTeX without errors before discarding the original
  120. bibliography file.
  121.  
  122. We do our own output and line buffering here so as to be able to
  123. trim trailing blanks, and output data in rather large blocks for
  124. efficiency (in filters of this type, I/O accounts for the bulk of
  125. the processing, so large output buffers offer significant
  126. performance gains).
  127.  
  128. The -scribe option enables recognition of the extended syntax used by
  129. the Scribe document formatting system, originally developed by Brian
  130. Reid at Carnegie-Mellon University, and now marketed by Unilogic, Ltd.
  131. I have followed the syntax description in the Scribe Introductory
  132. User's Manual, 3rd Edition, May 1980.
  133.  
  134. Scribe extensions include these features:
  135.  
  136. (1) Letter case is not significant in keywords and entry names, but
  137. case is preserved in value strings.
  138.  
  139. (2) In key/value pairs, the key and value may be separated by one of
  140. three characters: =, /, or space.  Space may optionally surround these
  141. separators.
  142.  
  143. (3) Value delimiters are any of these seven pairs:
  144. { }   [ ]   ( )      < >    ' '   " "   ` `
  145.  
  146. (4) Value delimiters may not be nested, even when with the first four
  147. delimiter pairs, nested balanced delimiters would be unambiguous.
  148.  
  149. (5) Delimiters can be omitted around values that contain only letters,
  150. digits, sharp (#), ampersand (&), period (.), and percent (%).
  151.  
  152. (6) A literal at-sign (@) is represented by doubled at-signs (@@).
  153.  
  154. (7) Bibliography entries begin with @name, as for BibTeX, but any of
  155. the seven Scribe value delimiters may be used to surround the
  156. key/value pairs.  As in (4), nested delimiters are forbidden.
  157.  
  158. (8) Arbitrary space may separate entry names from the following
  159. delimiters.
  160.  
  161. (9) @Comment is a special command whose delimited value is discared.
  162. As in (4), nested delimiters are forbidden.
  163.  
  164. (10) The special form
  165.  
  166. @Begin{comment}
  167. ...
  168. @End{comment}
  169.  
  170. permits encapsulating arbitrary text containing any characters or
  171. delimiters, other than "@End{comment}".     Any of the seven delimiters
  172. may be used around the word comment following the @begin or @end.
  173.  
  174. (11) The "key" keyword is required in each bibliography entry.
  175.  
  176. (12) Semicolons may be used in place of "and" in author lists
  177. (undocumented, but observed in practice).
  178.  
  179. Because of this loose syntax, error detection heuristics are less
  180. effective, and consequently, Scribe mode input is not the default; it
  181. must be explicitly requested.
  182.  
  183.  
  184. ========================================================================
  185.  
  186. ***********************************************************************/
  187.  
  188. #define BIBCLEAN_VERSION    "bibclean Version 2.05 [24-Nov-1992]"
  189.  
  190. /***********************************************************************
  191.  
  192. Revision history (reverse time order):
  193.  
  194. [16-Nov-1992 -- 24-Nov-1992]    2.05
  195.         Add Makefile steps to automatically extract help() text
  196.         from output of manual pages into new file bibclean.h, so
  197.         the built-in documentation stays up-to-date.  The usage
  198.         messages still need manual adjustment if switches are
  199.         added or changed.
  200.  
  201.         Add missing test of check_values in check_patterns().
  202.  
  203.         Add support for optional warning messages with
  204.         patterns from initialization files.  New function:
  205.         get_token().  New parsing code in do_new_pattern() to
  206.         handle optional warning message strings.  Add message
  207.         argument to add_pattern().
  208.  
  209.         Remove strip_comments() since comment processing is
  210.         now handled by get_token() and do_new_pattern().  This
  211.         permits unescaped comment characters inside quoted
  212.         strings.
  213.  
  214.         Write bibclean.reg, an initialization file similar to
  215.         bibclean.ini, but with regular expressions.
  216.  
  217.         Replace cascaded if statements for regular expression
  218.         testing with loop over patterns in check_patterns().
  219.  
  220.         Move inclusion of match.h to after definition of
  221.         typedef YESorNO, and change type of match_pattern()
  222.         from int to YESorNO.
  223.  
  224.         Add do_fileinit() and code in main() to call
  225.         do_fileinit() for each named input file with an
  226.         extension, replacing that extension with INITFILE_EXT
  227.         (default .ini).  This adds a bibliography-specific
  228.         initialization capability to the system-wide,
  229.         user-wide, and job-wide files already supported.
  230.  
  231.         Change -keep-initials and -keep-names to -fix-initials
  232.         and -fix-names, making them positive, rather than
  233.         negative, options.  Also, make them independent by
  234.         moving invocations of fix_period() outside of
  235.         fix_author(), and by checking fix_names in
  236.         fix_author() instead of at start of fix_namelist().
  237.  
  238.         Add -[no-]read-init-files option to allow control over
  239.         which initialization files are read.
  240.  
  241.         Add -[no-]trace-file-opening option to allow easy
  242.         tracing of file opening attempts by the program.  A
  243.         similar feature in my DVI drivers has proved enormously
  244.         valuable in tracking down problems of missing files.
  245.  
  246.         Rename entry_name[] to current_entry_name[], key[] to
  247.         current_key[], tag[] to current_tag[], and value[] to
  248.         current_value[] to get more distinctive names for
  249.         those global variables.
  250.  
  251.         Include the value string matching code selection in the
  252.         version() message; this is needed so that users can
  253.         prepare initialization files with the correct pattern
  254.         syntax.
  255.  
  256.         Make several MAX_xxx symbolic constants definable
  257.         at compile time.
  258.  
  259.         Add MAX_PATTERN_NAMES constant, and increase
  260.         pattern_names[] table to that size, leaving empty slots
  261.         for expansion.  Extend add_pattern() so that
  262.         unrecognized key names result in creation of new entries
  263.         in pattern_names[], making the set of key/value pairs
  264.         extensible without modification of the bibclean source
  265.         code.  Add check_other() to handle checking of other
  266.         keywords.
  267.  
  268.         Add unexpected() to localize issuing of unexpected value
  269.         warnings.
  270.  
  271.         Repair next_s() in match.c to skip past <backslash><non-letter>
  272.         TeX control sequence; it was stopping one character
  273.         early.
  274.  
  275.         Revise upper-case letter bracing code in fix_title()
  276.         to handle more cases.
  277.  
  278.         Rewrite space collapsing code in fix_pages() to only
  279.         collapse space around en-dashes.  The previous code
  280.         was too aggressive, so that "319 with 30 illustrations"
  281.         became "319 with30illustrations".
  282.  
  283.         Add check_tag() called from do_tag_name(), and add
  284.         second argument, value, to check_patterns().
  285.  
  286.         Add format() called from error() and warning() to
  287.         expand %e (@entry name), %k (key), %t (tag), %v
  288.         (value), and %% (percent) format items in messages.
  289.         This feature is needed so user-defined messages in
  290.         initialization files can get key, tag, and value into
  291.         messages.  It also simplifies, and improves, calls to
  292.         warning() and error().
  293.  
  294.         Add some missing (void) typecasts before str***() calls.
  295.  
  296.         Change word_length() to return one more than true
  297.         length at end of string.  Change tests in out_s() to
  298.         > MAX_COLUMN instead of >= MAX_COLUMN.  Previously, if
  299.         a line ended exactly at column MAX_COLUMN, bibclean
  300.         could produce a spurious blank line, and would
  301.         sometimes wrap a line earlier than necessary.  Add
  302.         additional punctuation wrap points in out_s(), and
  303.         remove tests for non-blank whitespace in switch()
  304.         statement.
  305.  
  306.         Change type of all string index variables from int to
  307.         size_t.
  308.  
  309.         In get_simple_string(), use enum type for type codes if
  310.         NEW_STYLE.
  311.  
  312.         In check_year(), validate all sequences of 1 or more
  313.         digits.
  314.  
  315.         Use the C preprocessor to define memmove() to be
  316.         Memmove(), so we always use our own version.  Too many
  317.         C and C++ implementations were found to be lacking it,
  318.         sigh...  Similarly, we provide our own version of
  319.         strtol() (in a separate file) from the DVI 3.0
  320.         development, because it too is missing from older UNIX
  321.         systems.
  322.  
  323.         Complete port to IBM PC DOS with Turbo C 2.0, and
  324.         Turbo C and C++ 3.0.  This required economization of
  325.         storage for arrays of size [MAX_TOKEN_SIZE] to get
  326.         global data below 64KB without having to reduce
  327.         MAX_TOKEN.
  328.  
  329.         Added code in do_more() and preprocessor conditionals
  330.         in out_lines() to handle character-at-a-time input for
  331.         help paging on IBM PC DOS. Keyboard function keys
  332.         PgUp, PgDn, End, Home, Up arrow and Down arrow are
  333.         also recognized.  This was easy to do because most PC
  334.         DOS C compilers provide getch() to get a keyboard
  335.         character without echo.  No fiddling of terminal modes
  336.         is needed like it is on other systems.
  337.  
  338.         The IBM PC DOS port exposed a problem in findfile(),
  339.         where it was assumed that an environment variable
  340.         would not be longer than the longest filename.  Turbo
  341.         C sets the latter to 80 characters, but environment
  342.         variables can be set that are almost 128 characters
  343.         long.  Microsoft C 5.0 also sets it to 80, but C 5.1
  344.         sets it to 144, and C 6.0 and C and C++ 7.0 set it to
  345.         260.  This has been handled by defining MAXPATHLEN at
  346.         compile time, overriding the built-in defaults.
  347.  
  348.         Add support for character-at-a-time input for help
  349.         paging on VAX VMS, and for getting the screen size in
  350.         get_screen_lines().
  351.  
  352.         Rename do_more_init() to kbopen(), do_more_term() to
  353.         kbclose(), and use kbget() in do_more() to conceal the
  354.         heavily-O/S dependent details of the kbxxx()
  355.         functions.
  356.  
  357.         Introduce STREQUAL() macro to simplify coding.
  358.  
  359.         Introduce KEY_FUNCTION_ENTRY type and apply_function()
  360.         to simplify coding, and use it in do_args(),
  361.         do_preargs(), and out_value().  Argument actions are
  362.         moved into separate functions, opt_xxx().  Rename
  363.         show_author() to opt_author(), and help() to
  364.         opt_help().  Rename do_file() to do_one_file(), and
  365.         move file loop code from main() into new do_files().
  366.  
  367.         Split large body of get_simple_string() into four new
  368.         functions, get_braced_string(), get_digit_string(),
  369.         get_quoted_string(), and get_identifier_string().
  370.  
  371.         Add check_inodes() to determine whether stdlog and
  372.         stdout are the same file.  If so, we need to ensure
  373.         that each warning message begins a new line, without
  374.         double spacing unnecessarily when they are different
  375.         files.
  376.  
  377.         Add memset() implementation for SunOS 4.1.1 CC (C++)
  378.         and BSD 4.3 UNIX because it is missing from their
  379.         run-time libraries.
  380.  
  381.         Replace fopen() by macro FOPEN() to work around
  382.         erroneous fopen() prototype for SunOS 4.1.1 CC (C++).
  383.  
  384.         Complete port to IBM PC DOS with Microsoft C 5.1 and
  385.         6.0 compilers.  Minor source changes (the CONST macro
  386.         below) needed to work around compiler errors.
  387.  
  388.  
  389. [15-Nov-1992]    2.04
  390.         Minor changes to complete successful VAX VMS
  391.         installation and test.
  392.  
  393.  
  394. [15-Nov-1992]    2.03
  395.         Add match_pattern() support for consistent pattern
  396.         matching in the check_xxx() functions, using new code
  397.         defined separately in match.c.
  398.  
  399.         Add support for run-time redefinition of patterns via
  400.         one or more initialization file(s) found in the PATH
  401.         (system-defined) and BIBINPUTS (user-defined) search
  402.         paths.  New functions: add_pattern(),
  403.         check_patterns(), do_initfile(), do_new_pattern(),
  404.         do_single_arg(), enlarge_table(), get_line(),
  405.         strdup(), strip_comments(), and trim_value().  New C
  406.         preprocessor symbols: HAVE_OLDCODE, HAVE_PATTERNS,
  407.         HAVE_RECOMP, and HAVE_REGEXP.  One of these should be
  408.         defined at compile time; if none are, then
  409.         HAVE_PATTERNS is the default.
  410.  
  411.         Since options can now be specified in initialization
  412.         files, they each need negations so the command line
  413.         can override values from an initialization file.
  414.  
  415.         Change all YES/NO flags to new type, YESorNO, for
  416.         better type checking.
  417.  
  418.         Add do_more(), do_more_init(), and do_more_term(), for
  419.         pausing during help output; a private version of
  420.         screen paging is used instead of a pager invoked by
  421.         system() for better portability across systems.  Set
  422.         SCREEN_LINES to 0 at compile time to suppress this
  423.         feature.
  424.  
  425.         In fix_title(), add code to brace upper-case letters
  426.         for cases like:
  427.             "X11" -> "{X11}"
  428.             "Standard C Library" -> "Standard {C} Library"
  429.             "C++ Book" -> "{C}++ Book"
  430.         leaving
  431.             "A xxx"
  432.         unchanged.
  433.  
  434.  
  435. [11-Nov-1992]    2.02
  436.         Add bad_ISBN(), bad_ISSN(), check_ISBN(), and
  437.         check_ISSN() for validation of ISBN and ISSN fields.
  438.         ISBN == "International Standard Book Number", and ISSN
  439.         = "International Standard Serial Number".
  440.  
  441.         Add testisxn.bib and testisxn.bok to the test
  442.         collection, with steps in the Makefile to run the
  443.         test.
  444.  
  445.         Add support for embedded \" in Scribe value strings
  446.         (forgotten in 2.01 revision); they are converted from
  447.         \"x to {\"x}.
  448.  
  449.  
  450. [10-Nov-1992]    2.01
  451.         Add support for conversion of level-0 \"x to {\"x} and
  452.         x"y to x{"}y in value strings.  Such input is illegal
  453.         for BibTeX, and causes hard-to-find errors, since
  454.         BibTeX raises an error at the line where it runs out
  455.         of string collection space, rather than at the
  456.         beginning of the collection point.
  457.  
  458.  
  459. [06-Nov-1992]    2.00
  460.         Add full Scribe .bib file input compatibility with
  461.         -scribe command-line option.
  462.  
  463.         Add support for multiple .bib file arguments on
  464.         command line, with new do_file() function to process
  465.         them.
  466.  
  467.         Allow slash as well as hyphen for introducing
  468.         command-line options on VAX VMS and IBM PC DOS.
  469.  
  470.         Add argument summary to help() (text extracted
  471.         verbatim from the manual pages).
  472.  
  473.         Add new -delete-empty-fields, -keep-names,
  474.         -no-parbreaks, -remove-OPT-prefixes, and -no-warnings
  475.         command-line options and support code.
  476.  
  477.         Add new out_with_error() and out_with_parbreak_error()
  478.         functions, and APPEND_CHAR() and EMPTY_STRING() macros
  479.         to shorten and clarify coding.
  480.  
  481.         Add flush_inter_entry_space() function to standardize
  482.         line spacing.
  483.  
  484.         Increase array sizes to MAX_TOKEN_SIZE (= MAX_TOKEN +
  485.         3) to reduce array bounds checking in inner loops.
  486.  
  487.         Add additional file position tracking to enhance error
  488.         localization (structures IO_PAIR and POSITION, and
  489.         functions new_io_pair(), new_position(),
  490.         out_position(), and out_status()).  Error messages are
  491.         parsable by GNU Emacs M-x next-error (C-x `) when
  492.         bibclean is run from Emacs by the command
  493.         M-x compile<RET>bibclean foo.bib >foo.new
  494.  
  495.         Use arrays of constant strings for multiple string
  496.         output via new function out_lines(), instead of multiple
  497.         calls to fprintf().
  498.  
  499.         Add additional checking via check_chapter(),
  500.         check_month(), check_number(), check_pages(),
  501.         check_volume(), check_year(), and match_regexp().
  502.  
  503.         Supply implementation of memmove() library function
  504.         missing from g++ 2.2.2 library.
  505.  
  506.  
  507. [03-Oct-1992]    1.06
  508.         Correct logic error in do_comma() that prevented correct
  509.         recognition of @name(key = "value") where the last
  510.         key/value pair did not have a trailing comma.
  511.  
  512.         Add C++ support.
  513.  
  514.         Add key_pair[] and entry_pair[] tables for
  515.         standardization of letter case usage, and use the new
  516.         NAME_PAIR type in fix_months().
  517.  
  518.         Update author address.
  519.  
  520.         Rename author() to show_author() to avoid shadowing
  521.         global names.
  522.  
  523.         Fix two assignments of constant strings to char*
  524.         pointers.
  525.  
  526.         Remove variable at_line_number which was defined, but
  527.         never used.
  528.  
  529. [01-Aug-1992]    1.05
  530.         Add -keep-initials switch support (thanks to Karl Berry
  531.         <karl@cs.umb.edu>).  Internationalize telephone and FAX
  532.         numbers.
  533.  
  534. [02-Jan-1992]    1.04
  535.         Modify fix_title() to ignore macros.  Modify
  536.         fix_author()) to ignore author lists with parentheses
  537.         (e.g.  author = "P. D. Q. Bach (113 MozartStrasse,
  538.         Vienna, Austria)").
  539.  
  540. [31-Dec-1991]    1.03
  541.         Add fix_title() to supply braces around unbraced
  542.         upper-case acronyms in titles, and add private
  543.         definition of MAX().
  544.  
  545. [15-Nov-1991]    1.02
  546.         Handle @String(...) and @Preamble(...), converting
  547.         outer parentheses to braces.  Insert spaces after
  548.         author and editor initials, and normalize names to
  549.         form "P. D. Q. Bach" instead of "Bach, P. D. Q.".
  550.  
  551. [10-Oct-1991]    1.01
  552.         Increase MAX_TOKEN to match enlarged BibTeX, and add
  553.         check against STD_MAX_TOKEN.
  554.         Output ISBN and ISSN in upper case.
  555.         Always surround = by blanks in key = "value".
  556.  
  557. [19-Dec-1990]    1.00 (version number unchanged)
  558.         Install Sun386i bug fix.
  559.  
  560. [08-Oct-1990]    1.00
  561.         Original version.
  562. ***********************************************************************/
  563.  
  564. /* Make a preliminary sanity check on which pattern matching we will use */
  565.  
  566. #if defined(HAVE_REGEXP)
  567. #if defined(HAVE_RECOMP) || defined(HAVE_PATTERNS) || defined(HAVE_OLDCODE)
  568. ?? Define only one of HAVE_OLDCODE, HAVE_PATTERNS, HAVE_REGEXP, and HAVE_RECOMP
  569. #endif
  570. #endif
  571.  
  572. #if defined(HAVE_RECOMP)
  573. #if defined(HAVE_REGEXP) || defined(HAVE_PATTERNS) || defined(HAVE_OLDCODE)
  574. ?? Define only one of HAVE_OLDCODE, HAVE_PATTERNS, HAVE_REGEXP, and HAVE_RECOMP
  575. #endif
  576. #endif
  577.  
  578. #if defined(HAVE_PATTERNS)
  579. #if defined(HAVE_RECOMP) || defined(HAVE_REGEXP) || defined(HAVE_OLDCODE)
  580. ?? Define only one of HAVE_OLDCODE, HAVE_PATTERNS, HAVE_REGEXP, and HAVE_RECOMP
  581. #endif
  582. #endif
  583.  
  584. #if defined(HAVE_OLDCODE)
  585. #if defined(HAVE_PATTERNS) || defined(HAVE_RECOMP) || defined(HAVE_REGEXP)
  586. ?? Define only one of HAVE_OLDCODE, HAVE_PATTERNS, HAVE_REGEXP, and HAVE_RECOMP
  587. #endif
  588. #endif
  589.  
  590. #if !(defined(HAVE_REGEXP) || defined(HAVE_RECOMP))
  591. #if !(defined(HAVE_PATTERNS) || defined(HAVE_OLDCODE))
  592. #define HAVE_PATTERNS    1
  593. #endif
  594. #endif
  595.  
  596. /***********************************************************************
  597. We want this code to be compilable with C++ compilers as well as C
  598. compilers, in order to get better compile-time checking.  We therefore
  599. must declare all function headers in both old Kernighan-and-Ritchie
  600. style, as well as in new Standard C and C++ style.  Although Standard C
  601. also allows K&R style, C++ does not.
  602.  
  603. For functions with no argument, we just use VOID which expands to either
  604. void, or nothing.
  605.  
  606. Older C++ compilers predefined the symbol c_plusplus, but that was
  607. changed to __cplusplus in 1989 to conform to ISO/ANSI Standard C
  608. conventions; we allow either.
  609.  
  610. It is regrettable that the C preprocessor language is not powerful
  611. enough to transparently handle the generation of either style of
  612. function declaration.
  613. ***********************************************************************/
  614.  
  615. #include "os.h"
  616. #include "xstdlib.h"
  617. #include "xstring.h"
  618. #include "xctype.h"
  619. #include "xstat.h"
  620. #include "unixlib.h"
  621.  
  622. RCSID("$Id: bibclean.c,v 1.15 1992/11/24 15:37:18 beebe Exp beebe $")
  623.  
  624. /* $Log: bibclean.c,v $
  625.  * Revision 1.15  1992/11/24  15:37:18  beebe
  626.  * Incorporate changes for Microsoft C compiler port.
  627.  *
  628.  * Revision 1.14  1992/11/22  17:46:27  beebe
  629.  * Update for version 2.05.  See internal change log for extensive list of
  630.  * changes.
  631.  *
  632.  * Revision 1.13  1992/11/15  16:54:39  beebe
  633.  * Got interrupted and forgot to update file header.
  634.  *
  635.  * Revision 1.12  1992/11/15  16:53:28  beebe
  636.  * Update for VAX VMS.
  637.  *
  638.  * Revision 1.11  1992/11/15  08:20:55  beebe
  639.  * Complete version 2.03; details are in the internal change log.
  640.  * */
  641.  
  642. #if defined(memmove)
  643. #undef memmove                /* at least one system defines this */
  644. #endif
  645. #define memmove    Memmove            /* we want our private version */
  646.                     /* see 2.05 change log above for why */
  647.  
  648. #define NEW_STYLE    (__cplusplus || __STDC__ || c_plusplus)
  649.  
  650. #if NEW_STYLE
  651. #define VOID    void
  652. #else /* K&R style */
  653. #define VOID
  654. #endif /* NEW_STYLE */
  655.  
  656. #if NEW_STYLE
  657. typedef enum { NO = 0, YES = 1 } YESorNO;
  658. #else /* K&R style */
  659. #define NO  0                /* must be FALSE (zero) */
  660. #define YES 1                /* must be TRUE (non-zero) */
  661. typedef int YESorNO;
  662. #endif /* NEW_STYLE */
  663.  
  664. #include "match.h"            /* must come after YESorNO typedef */
  665.  
  666. #if M_I86
  667. #define CONST        /* bug workaround for IBM PC Microsoft C compilers */
  668. #else /* NOT M_I86 */
  669. #define CONST const
  670. #endif /* M_I86 */
  671.  
  672. typedef struct s_key_function_entry
  673. {
  674.     const char *name;            /* key name */
  675.     size_t min_match;            /* minimum length string match */
  676.     void (*function)(VOID);        /* function to call when key matched */
  677. } KEY_FUNCTION_ENTRY;
  678.  
  679. typedef struct s_name_pair
  680. {
  681.     const char *old_name;
  682.     const char *new_name;
  683. } NAME_PAIR;
  684.  
  685. typedef struct s_position
  686. {
  687.     const char *filename;
  688.     long byte_position;
  689.     long last_column_position;
  690.     long column_position;
  691.     long line_number;
  692. } POSITION;
  693.  
  694. typedef struct s_io_pair
  695. {
  696.     POSITION input;
  697.     POSITION output;
  698. } IO_PAIR;
  699.  
  700. typedef struct s_pattern_table
  701. {
  702.     MATCH_PATTERN *patterns;
  703.     int current_size;
  704.     int maximum_size;
  705. } PATTERN_TABLE;
  706.  
  707. typedef struct s_pattern_names
  708. {
  709.     const char *name;
  710.     PATTERN_TABLE *table;
  711. } PATTERN_NAMES;
  712.  
  713. #if defined(sun386)
  714. /* Sun386i run-time library bug in fputs(): only first line in s is written! */
  715. #define fputs(s,fp) fwrite(s,1,strlen(s),fp)
  716. #endif
  717.  
  718. #define    APPEND_CHAR(s,n,c)    (s[n] = (char)c, s[n+1] = (char)'\0')
  719.                 /* append c and NUL to s[] */
  720.  
  721. #define COMMENT_PREFIX    '%'    /* comment character in initialization files */
  722.  
  723. #define CTL(X)        (X & 037)    /* make ASCII control character */
  724.  
  725. #define DELETE_CHAR    (EOF - 1) /* magic char value for put_char() */
  726.  
  727. #define DELETE_LINE    (EOF - 2) /* magic char value for put_char() */
  728.  
  729. #define EMPTY_STRING(s)    (s[0] = (char)'\0', s)
  730.                 /* for return (EMPTY_STRING(foo))*/
  731.  
  732. #define    ERROR_PREFIX    "??"    /* this prefixes all error messages */
  733.  
  734. #if !defined(EXIT_FAILURE)
  735. #define EXIT_FAILURE 1
  736. #endif
  737.  
  738. #if !defined(EXIT_SUCCESS)
  739. #define EXIT_SUCCESS 0
  740. #endif
  741.  
  742. #undef FOPEN
  743. #if defined(__SUNCC__)
  744. #define FOPEN(a,b) fopen((char*)(a),(char*)(b))
  745.     /* bug workaround: wrong type for fopen() args with SunOS 4.1.2 CC */
  746. #else /* NOT defined(__SUNCC__) */
  747. #define FOPEN(a,b) fopen((a),(b))
  748. #endif /* defined(__SUNCC__) */
  749.  
  750. #if !defined(INITFILE_EXT)
  751. #define INITFILE_EXT    ".ini"    /* file extension for initialization files */
  752. #endif
  753.  
  754. #define ISBN_DIGIT_VALUE(c)    ((((c) == 'X') || ((c) == 'x')) ? 10 : \
  755.                     ((c) - '0'))
  756.                 /* correct only if digits are valid; */
  757.                 /* the code below ensures that */
  758.  
  759. #define ISSN_DIGIT_VALUE(c)    ISBN_DIGIT_VALUE(c)
  760.                 /* ISSN digits are just like ISBN digits */
  761.  
  762. #define KEY_INDENTATION    2    /* how far to indent "key = value," pairs */
  763.  
  764. #define LAST_SCREEN_LINE (-2)    /* used in opt_help() and do_more() */
  765.  
  766. #if defined(MAX)
  767. #undef MAX
  768. #endif
  769.  
  770. #define MAX(a,b)    (((a) > (b)) ? (a) : (b))
  771.  
  772. #if !defined(MAX_BUFFER)
  773. #define MAX_BUFFER    8192    /* output buffer size; this does NOT */
  774.                 /* limit lengths of input lines */
  775. #endif /* !defined(MAX_BUFFER) */
  776.  
  777. #if !defined(MAX_COLUMN)
  778. #define MAX_COLUMN    72    /* length of longest entry line; */
  779.                 /* non-BibTeX entry text is output verbatim */
  780. #endif /* !defined(MAX_COLUMN) */
  781.  
  782. #if !defined(MAX_KEY_LENGTH)
  783. #define MAX_KEY_LENGTH    12    /* "howpublished" */
  784. #endif /* !defined(MAX_KEY_LENGTH) */
  785.  
  786. #if !defined(MAX_LINE)
  787. #define MAX_LINE    10240    /* maximum line length in initialization file */
  788. #endif /* !defined(MAX_LINE) */
  789.  
  790. #if !defined(MAX_PATTERN_NAMES)
  791. #define MAX_PATTERN_NAMES 100    /* maximum number of key/pattern types; */
  792.                 /* 100 is far more than ever likely to be */
  793.                 /* needed, but we only waste 8 bytes each for */
  794.                 /* unused entries */
  795. #endif /* !defined(MAX_PATTERN_NAMES) */
  796.  
  797. #if !defined(MAX_TOKEN)
  798. #define MAX_TOKEN    4093    /* internal buffer size; no BibTeX string
  799.                 value may be larger than this. */
  800. #endif /* !defined(MAX_TOKEN) */
  801.  
  802. #define MAX_TOKEN_SIZE    (MAX_TOKEN + 3)    /* Arrays are always dimensioned
  803.                 MAX_TOKEN_SIZE, so as to have space
  804.                 for an additional pair of braces and a
  805.                 trailing NUL, without tedious
  806.                 subscript checking in inner loops. */
  807.  
  808. #define META(X)        (X | 0200)    /* make GNU Emacs meta character */
  809.  
  810. #define NOOP            /* dummy statement */
  811.  
  812. #if defined(HAVE_PATTERNS)
  813. #define PATTERN_MATCHES(string,pattern) (match_pattern(string,pattern) == YES)
  814. #else /* NOT defined(HAVE_PATTERNS) */
  815. #define PATTERN_MATCHES(string,pattern) match_regexp(string,pattern)
  816. #endif /* defined(HAVE_PATTERNS) */
  817.  
  818. #if !defined(SCREEN_LINES)
  819. #if OS_PCDOS
  820. #define SCREEN_LINES    25    /* set 0 to disable pausing in out_lines() */
  821. #else /* NOT OS_PCDOS */
  822. #define SCREEN_LINES    24    /* set 0 to disable pausing in out_lines() */
  823. #endif /* OS_PCDOS */
  824. #endif /* !defined(SCREEN_LINES) */
  825.  
  826. #define SKIP_NONSPACE(p) while (*p && !isspace(*p)) ++p
  827.  
  828. #define SKIP_SPACE(p)    while (isspace(*p)) ++p
  829.  
  830. #define STD_MAX_TOKEN    ((size_t)1000)    /* Standard BibTeX limit */
  831.  
  832. #define STREQUAL(a,b)    (strcmp(a,b) == 0)
  833.  
  834. #define TABLE_CHUNKS    25    /* how many table entries to allocate at once */
  835.  
  836. #define TOLOWER(c)      (isupper(c) ? tolower(c) : (c))
  837.  
  838. #define VALUE_INDENTATION    (KEY_INDENTATION + MAX_KEY_LENGTH + 3)
  839.                 /* where item values are output; allow space */
  840.                 /* for "<key indent><key name>< = >" */
  841.  
  842. #define WARNING_PREFIX    "%%"    /* this prefixes all warning messages */
  843.  
  844. /* Operating system-specific customizations. */
  845.  
  846. #if OS_UNIX
  847. #if !defined(INITFILE)
  848. #define INITFILE    ".bibcleanrc"
  849. #endif
  850.  
  851. #if !defined(SYSPATH)
  852. #define SYSPATH        "PATH"
  853. #endif
  854.  
  855. #if !defined(USERPATH)
  856. #define USERPATH    "BIBINPUTS"
  857. #endif
  858.  
  859. #define isoptionprefix(c)    ((c) == '-')
  860.  
  861. #endif /* OS_UNIX */
  862.  
  863. #if OS_VAXVMS
  864. #if !defined(INITFILE)
  865. #define INITFILE    "bibclean.ini"
  866. #endif
  867.  
  868. #if !defined(SYSPATH)
  869. #define SYSPATH        "SYS$SYSTEM"
  870. #endif
  871.  
  872. #if !defined(USERPATH)
  873. #define USERPATH    "BIBINPUTS"
  874. #endif
  875.  
  876. #define isoptionprefix(c)    (((c) == '-') || ((c) == '/'))
  877.  
  878. #endif /* OS_VAXVMS */
  879.  
  880. #if OS_PCDOS
  881. #define isoptionprefix(c)    (((c) == '-') || ((c) == '/'))
  882. #endif /* OS_PCDOS */
  883.  
  884. /* For any that are undefined, default to values suitable for OS_PCDOS. */
  885. #if !defined(INITFILE)
  886. #define INITFILE    "bibclean.ini"
  887. #endif
  888.  
  889. #if !defined(SYSPATH)
  890. #define SYSPATH        "PATH"
  891. #endif
  892.  
  893. #if !defined(USERPATH)
  894. #define USERPATH    "BIBINPUTS"
  895. #endif
  896.  
  897. /* All functions except main() are static to overcome limitations on
  898. external name lengths in ISO/ANSI Standard C.  Please keep them in
  899. ALPHABETICAL order, ignoring letter case. */
  900.  
  901. static void    add_one_pattern ARGS((PATTERN_TABLE *pt_, const char *keyname_,
  902.             const char *pattern_, const char *msg_));
  903. static void    add_pattern ARGS((const char *keyname_, const char *pattern_,
  904.             const char *msg_));
  905. static YESorNO    apply_function ARGS((const char *key_,
  906.             KEY_FUNCTION_ENTRY table_[]));
  907. static void    bad_ISBN ARGS((char ISBN_[11]));
  908. static void    bad_ISSN ARGS((char ISSN_[9]));
  909. static void    check_chapter ARGS((void));
  910. static void    check_inodes ARGS((void));
  911. static void    check_ISBN ARGS((void));
  912. static void    check_ISSN ARGS((void));
  913. static void    check_length ARGS((size_t n_));
  914. static void    check_month ARGS((void));
  915. static void    check_number ARGS((void));
  916. static void    check_other ARGS((void));
  917. static void    check_pages ARGS((void));
  918. static YESorNO    check_patterns ARGS((PATTERN_TABLE *pt_,const char *value_));
  919. static void    check_tag ARGS((void));
  920. static void    check_volume ARGS((void));
  921. static void    check_year ARGS((void));
  922. static void    do_args ARGS((int argc_, char *argv_[]));
  923. static void    do_at ARGS((void));
  924. static void    do_BibTeX_entry ARGS((void));
  925. static void    do_BibTeX_value ARGS((void));
  926. static void    do_close_brace ARGS((void));
  927. static void    do_comma ARGS((void));
  928. static void    do_entry_name ARGS((void));
  929. static void    do_equals ARGS((void));
  930. static void    do_escapes ARGS((char *s_));
  931. static void    do_files ARGS((int argc_, char *argv_[]));
  932. static void    do_fileinit ARGS((const char *bibfilename_));
  933. static void    do_group ARGS((void));
  934. static void    do_initfile ARGS((const char *pathlist_,const char *name_));
  935. static void    do_key ARGS((void));
  936. static YESorNO    do_key_value_pair ARGS((void));
  937.  
  938. #if (SCREEN_LINES > 0)
  939. static int    do_more ARGS((FILE *fpout_, int line_, int pause_after_));
  940. #endif /* (SCREEN_LINES > 0) */
  941.  
  942. static void    do_new_pattern ARGS((char *s_));
  943. static void    do_one_file ARGS((FILE *fp_));
  944. static void    do_open_brace ARGS((void));
  945. static void    do_other ARGS((void));
  946. static void    do_preargs ARGS((int argc_, char *argv_[]));
  947. static void    do_Scribe_block_comment ARGS((void));
  948. static void    do_Scribe_close_delimiter ARGS((void));
  949. static void    do_Scribe_comment ARGS((void));
  950. static void    do_Scribe_entry ARGS((void));
  951. static void    do_Scribe_open_delimiter ARGS((void));
  952. static void    do_Scribe_separator ARGS((void));
  953. static void    do_Scribe_value ARGS((void));
  954. static void    do_single_arg ARGS((char *s_));
  955. static void    do_tag_name ARGS((void));
  956. static void    enlarge_table ARGS((PATTERN_TABLE *table_));
  957. static void    error ARGS((const char *msg_));
  958. static void    fatal ARGS((const char *msg_));
  959. char        *findfile ARGS((const char *pathlist_, const char *name_));
  960. static char    *fix_author ARGS((char *author_));
  961. static void    fix_month ARGS((void));
  962. static void    fix_namelist ARGS((void));
  963. static void    fix_pages ARGS((void));
  964. static char    *fix_periods ARGS((char *author_));
  965. static void    fix_title ARGS((void));
  966. static void    flush_inter_entry_space ARGS((void));
  967. static char    *format ARGS((const char *msg_));
  968. static char    *get_braced_string ARGS((void));
  969. static int    get_char ARGS((void));
  970. static char    *get_digit_string ARGS((void));
  971. static char    *get_identifier_string ARGS((void));
  972. static char    *get_line ARGS((FILE *fp_));
  973. static int    get_next_non_blank ARGS((void));
  974. static char    *get_quoted_string ARGS((void));
  975.  
  976. #if (SCREEN_LINES > 0)
  977. static int    get_screen_lines ARGS((void));
  978. #endif /* (SCREEN_LINES > 0) */
  979.  
  980. static char    *get_Scribe_delimited_string ARGS((void));
  981. static char    *get_Scribe_identifier_string ARGS((void));
  982. static char    *get_Scribe_string ARGS((void));
  983. static char    *get_simple_string ARGS((void));
  984. static char    *get_token ARGS((char *s_, char **nextp_,
  985.             const char *terminators_));
  986. static int    isidchar ARGS((int c_));
  987.  
  988. #define iskeyvalueseparator(c)    (((c) == '=') || ((c) == ':'))
  989.  
  990. #if (SCREEN_LINES > 0)
  991. #if NEW_STYLE
  992. typedef enum key_code {
  993.     KEY_EOF = EOF,
  994.     KEY_UNKNOWN = 0,
  995.     KEY_AGAIN,
  996.     KEY_DOWN,
  997.     KEY_END,
  998.     KEY_HELP,
  999.     KEY_HOME,
  1000.     KEY_PGDN,
  1001.     KEY_PGUP,
  1002.     KEY_QUIT,
  1003.     KEY_UP
  1004. } KEYCODE;
  1005. #else /* K&R style */
  1006. #define KEY_EOF            EOF
  1007. #define KEY_UNKNOWN        0
  1008. #define KEY_AGAIN        1
  1009. #define KEY_DOWN        2
  1010. #define KEY_END            3
  1011. #define KEY_HELP        4
  1012. #define KEY_HOME        5
  1013. #define KEY_PGDN        6
  1014. #define KEY_PGUP        7
  1015. #define KEY_QUIT        8
  1016. #define KEY_UP            9
  1017. typedef int KEYCODE;
  1018. #endif /* NEW_STYLE */
  1019.  
  1020. #define MAX_CHAR        256
  1021.  
  1022. KEYCODE        keymap[MAX_CHAR];
  1023.  
  1024. static void    kbclose ARGS((void));
  1025. static KEYCODE    kbcode ARGS((void));
  1026. static int    kbget ARGS((void));
  1027. static void    kbinitmap ARGS((void));
  1028. static void    kbopen ARGS((void));
  1029. #endif /* (SCREEN_LINES > 0) */
  1030.  
  1031. int        main ARGS((int argc_, char *argv_[]));
  1032.  
  1033. #if (defined(HAVE_REGEXP) || defined(HAVE_RECOMP))
  1034. static int    match_regexp  ARGS((const char *string_,const char *pattern_));
  1035. #endif /* (defined(HAVE_REGEXP) || defined(HAVE_RECOMP)) */
  1036.  
  1037. /* NB: memmove() is a private version known as Memmove() to the compiler */
  1038. static void    memmove ARGS((void *target_, const void *source_, size_t n_));
  1039. static void    new_entry ARGS((void));
  1040. static void    new_io_pair ARGS((IO_PAIR *pair_));
  1041. static void    new_position ARGS((POSITION *position_));
  1042. static void    opt_author ARGS((void));
  1043. static void    opt_check_values ARGS((void));
  1044. static void    opt_delete_empty_fields ARGS((void));
  1045. static void    opt_error_log ARGS((void));
  1046. static void    opt_file_position ARGS((void));
  1047. static void    opt_fix_initials ARGS((void));
  1048. static void    opt_fix_names ARGS((void));
  1049. static void    opt_help ARGS((void));
  1050. static void    opt_init_file ARGS((void));
  1051. static void    opt_parbreaks ARGS((void));
  1052. static void    opt_print_patterns ARGS((void));
  1053. static void    opt_read_init_files ARGS((void));
  1054. static void    opt_remove_OPT_prefixes ARGS((void));
  1055. static void    opt_scribe ARGS((void));
  1056. static void    opt_trace_file_opening ARGS((void));
  1057. static void    opt_version ARGS((void));
  1058. static void    opt_warnings ARGS((void));
  1059.  
  1060. #define out_c(c_)    put_char(c_)    /* out_c() no longer a function */
  1061.  
  1062. static void    out_equals ARGS((void));
  1063. static void    out_error ARGS((FILE *fpout_, const char *s_));
  1064. static void    out_flush ARGS((void));
  1065. static void    out_key ARGS((void));
  1066. static void    out_lines ARGS((FILE *fpout_,const char *lines_[],
  1067.             YESorNO pause_));
  1068. static void    out_position ARGS((FILE *fpout_,const char *msg_,
  1069.             IO_PAIR *the_location_));
  1070. static void    out_s ARGS((const char *s_));
  1071. static void    out_spaces ARGS((int n_));
  1072. static void    out_status ARGS((FILE *fpout_, const char *prefix_));
  1073. static void    out_value ARGS((void));
  1074. static void    out_with_error ARGS((const char *s_,const char *msg_));
  1075. static void    out_with_parbreak_error ARGS((char *s_));
  1076. static void    prt_pattern ARGS((const char *keyname_, const char *pattern_,
  1077.             const char *msg_));
  1078. static void    put_back ARGS((int c_));
  1079. static void    put_back_string ARGS((const char *s_));
  1080. static void    put_char ARGS((int c_));
  1081. static void    resync ARGS((void));
  1082. char        *strdup ARGS((const char *s_));
  1083.  
  1084. int        strnicmp ARGS((const char *s1_, const char *s2_, size_t n_));
  1085.  
  1086. static FILE    *tfopen ARGS((const char *filename_, const char *mode_));
  1087. static void    trim_value ARGS((void));
  1088. static void    unexpected ARGS((void));
  1089. static void    usage ARGS((void));
  1090. static void    version ARGS((void));
  1091. static void    warning ARGS((const char *msg_));
  1092. static int    word_length ARGS((const char *s_));
  1093. static void    wrap_line ARGS((void));
  1094. static YESorNO    YESorNOarg ARGS((void));
  1095.  
  1096.  
  1097. /**********************************************************************/
  1098.  
  1099. /* All global variables are static to keep them local to this file,
  1100. and to overcome limitations on external name lengths in ISO/ANSI
  1101. Standard C.  Please keep them in ALPHABETICAL order, ignoring letter
  1102. case. */
  1103.  
  1104. static int    at_level = 0;            /* @ nesting level */
  1105. static int    brace_level = 0;        /* curly brace nesting level */
  1106. static YESorNO    check_values = YES;        /* NO: suppress value checks */
  1107. static int    close_char = EOF;        /* BibTeX entry closing; may */
  1108.                         /* be right paren or brace */
  1109. static char    current_entry_name[MAX_TOKEN_SIZE]; /* entry name */
  1110. static int    current_index;            /* argv[] index in do_args() */
  1111. static char    current_key[MAX_TOKEN_SIZE];    /* key name */
  1112. static char    *current_option;        /* set by do_args() */
  1113. static char    current_tag[MAX_TOKEN_SIZE];     /* citation tag */
  1114. static char    current_value[MAX_TOKEN_SIZE];    /* string value */
  1115. static YESorNO    delete_empty_fields = NO;     /* YES: delete empty fields */
  1116. static YESorNO    discard_next_comma = NO;     /* YES: deleting key/value */
  1117. static YESorNO    eofile = NO;            /* set to YES at end-of-file */
  1118. static int    error_count = 0;        /* used to decide exit code */
  1119.                         /* normalizing names */
  1120. #if defined(DEBUG)
  1121. static FILE    *fpdebug;            /* for debugging */
  1122. #endif /* defined(DEBUG) */
  1123.  
  1124. static FILE    *fpin;                /* input file pointer */
  1125.  
  1126. static char    *initialization_file_name;
  1127. static YESorNO    is_parbreak = NO;        /* get_next_non_blank() sets */
  1128. static YESorNO    fix_initials = YES;        /* reformat A.U. Thor?    */
  1129. static YESorNO    fix_names = YES;        /* reformat Bach, P.D.Q? */
  1130.  
  1131. static NAME_PAIR month_pair[] =
  1132. {
  1133.     {"\"January\"",     "jan",},
  1134.     {"\"February\"", "feb",},
  1135.     {"\"March\"",     "mar",},
  1136.     {"\"April\"",     "apr",},
  1137.     {"\"May\"",     "may",},
  1138.     {"\"June\"",     "jun",},
  1139.     {"\"July\"",     "jul",},
  1140.     {"\"August\"",     "aug",},
  1141.     {"\"September\"","sep",},
  1142.     {"\"October\"",     "oct",},
  1143.     {"\"November\"", "nov",},
  1144.     {"\"December\"", "dec",},
  1145. };
  1146.  
  1147. static char    *next_option;            /* set in do_args() */
  1148. static int    non_white_chars = 0;        /* used to test for legal @ */
  1149. static YESorNO    parbreaks = YES;        /* NO: parbreaks forbidden */
  1150.                         /* in strings and entries */
  1151. static YESorNO    print_patterns = NO;        /* YES: print value patterns */
  1152. static char    *program_name;            /* set to argv[0] */
  1153.  
  1154. static PATTERN_TABLE pt_chapter = { (MATCH_PATTERN*)NULL, 0, 0 };
  1155. static PATTERN_TABLE pt_month   = { (MATCH_PATTERN*)NULL, 0, 0 };
  1156. static PATTERN_TABLE pt_number  = { (MATCH_PATTERN*)NULL, 0, 0 };
  1157. static PATTERN_TABLE pt_pages   = { (MATCH_PATTERN*)NULL, 0, 0 };
  1158. static PATTERN_TABLE pt_volume  = { (MATCH_PATTERN*)NULL, 0, 0 };
  1159. static PATTERN_TABLE pt_year    = { (MATCH_PATTERN*)NULL, 0, 0 };
  1160.  
  1161. static PATTERN_NAMES pattern_names[MAX_PATTERN_NAMES] =
  1162. {
  1163.     {"chapter",        &pt_chapter},
  1164.     {"month",        &pt_month},
  1165.     {"number",        &pt_number},
  1166.     {"pages",        &pt_pages},
  1167.     {"volume",        &pt_volume},
  1168.     {"year",        &pt_year},
  1169. #if _AIX370
  1170.     {NULL,        NULL},    /* CC compiler cannot handle correct cast */
  1171. #else /* NOT _AIX370 */
  1172.     {(CONST char*)NULL,    (PATTERN_TABLE*)NULL}, /* entry terminator */
  1173. #endif /* _AIX370 */
  1174.  
  1175.     /* remaining slots may be initialized at run time */
  1176. };
  1177.  
  1178. static YESorNO    read_initialization_files = YES;/* -[no]-read-init-files sets */
  1179. static YESorNO    remove_OPT_prefixes = NO;     /* YES: remove OPT prefix */
  1180. static YESorNO    rflag = NO;            /* YES: resynchronizing */
  1181. static int    screen_lines = SCREEN_LINES;/* kbopen() and out_lines() reset */
  1182. static YESorNO    Scribe = NO;            /* Scribe format input */
  1183. static char    Scribe_open_delims[]  = "{[(<'\"`";
  1184. static char    Scribe_close_delims[] = "}])>'\"`";
  1185.  
  1186. /* In all memory models from tiny to huge, Turbo C on IBM PC DOS will
  1187. not permit more than 64KB of global constant data.  Therefore, we use a
  1188. global scratch array shared between the functions fix_title(),
  1189. format(), get_Scribe_identifier_string() and
  1190. get_Scribe_delimited_string().  The code has been carefully examined
  1191. to make sure that this space is not overwritten while still in use.
  1192. Oh, the pain of the Intel segmented memory architecture! */
  1193.  
  1194. static char    shared_string[MAX_TOKEN_SIZE];
  1195.  
  1196. static YESorNO    show_file_position = NO;     /* messages usually brief */
  1197. static FILE    *stdlog;            /* usually stderr */
  1198. YESorNO        stdlog_on_stdout = YES;        /* NO for separate files */
  1199.  
  1200. #if OS_PCDOS
  1201. unsigned int _stklen = 0xF000;            /* stack size for Turbo C */
  1202. #endif /* OS_PCDOS */
  1203.  
  1204. static IO_PAIR    the_entry;            /* used in error messages */
  1205. static IO_PAIR    the_file;            /* used in error messages */
  1206. static IO_PAIR    the_value;            /* used in error messages */
  1207.  
  1208. static YESorNO    trace_file_opening = NO; /* -[no-]trace-file-opening sets */
  1209. static YESorNO    warnings = YES;            /* NO: suppress warnings */
  1210.  
  1211. /**********************************************************************/
  1212.  
  1213.  
  1214. #if NEW_STYLE
  1215. static void
  1216. add_one_pattern(PATTERN_TABLE *pt, const char *keyname, const char *pattern,
  1217.     const char *message)
  1218. #else /* K&R style */
  1219. static void
  1220. add_one_pattern(pt,keyname,pattern,message)
  1221. PATTERN_TABLE *pt;
  1222. const char *keyname;
  1223. const char *pattern;
  1224. const char *message;
  1225. #endif /* NEW_STYLE */
  1226. {
  1227.     int m;                /* index into pt->patterns[] */
  1228.  
  1229.     if (STREQUAL(pattern,""))        /* then clear pattern table */
  1230.     {
  1231.     for (m = 0; m < pt->current_size; ++m)
  1232.     {            /* free old pattern memory */
  1233.         if (pt->patterns[m].pattern != (char*)NULL)
  1234.         free((char*)pt->patterns[m].pattern);
  1235.                 /* NB: (void*) cast fails with Sun C++ */
  1236.         if (pt->patterns[m].message != (char*)NULL)
  1237.         free((char*)pt->patterns[m].message);
  1238.     }
  1239.     pt->current_size = 0;
  1240.     }
  1241.     else                /* otherwise add new pattern */
  1242.     {
  1243.     if (pt->current_size == pt->maximum_size) /* then table full */
  1244.         enlarge_table(pt);
  1245.     for (m = 0; m < pt->current_size; ++m)
  1246.     {
  1247.         /* Make sure this is not a duplicate; if it is, and its message */
  1248.         /* is the same, then we just ignore the request.  Duplicates */
  1249.         /* are possible when the user and system search paths overlap. */
  1250.         if (STREQUAL(pattern,pt->patterns[m].pattern))
  1251.         {                /* duplicate pattern found */
  1252.         if (((pt->patterns[m].message) != (char*)NULL)
  1253.             && (message != (char*)NULL) &&
  1254.             (STREQUAL(message,pt->patterns[m].message)))
  1255.             return;        /* messages duplicate too */
  1256.         pt->patterns[m].message =
  1257.             (message == (char*)NULL) ? message :
  1258.                 (const char*)strdup(message);
  1259.                     /* replace message string */
  1260.         prt_pattern(keyname,pattern,message);
  1261.         return;
  1262.         }
  1263.     }
  1264.     /* We have a new and distinct pattern and message, so save them */
  1265.     pt->patterns[pt->current_size].pattern = strdup(pattern);
  1266.     pt->patterns[pt->current_size++].message =
  1267.         (message == (char*)NULL) ? message : (const char*)strdup(message);
  1268.     }
  1269.     prt_pattern(keyname,pattern,message);
  1270. }
  1271.  
  1272.  
  1273. #if NEW_STYLE
  1274. static void
  1275. add_pattern(const char *keyname, const char *pattern, const char *message)
  1276. #else /* K&R style */
  1277. static void
  1278. add_pattern(keyname,pattern,message)
  1279. const char *keyname;
  1280. const char *pattern;
  1281. const char *message;
  1282. #endif /* NEW_STYLE */
  1283. {
  1284.     int k;                /* index into pattern_names[] */
  1285.     size_t n = strlen(keyname);        /* saved keyname string length */
  1286.  
  1287.     for (k = 0; pattern_names[k].name != (const char*)NULL; ++k)
  1288.     {                    /* find the correct pattern table */
  1289.     if (strnicmp(pattern_names[k].name,keyname,n) == 0)
  1290.     {                /* then found the required table */
  1291.         add_one_pattern(pattern_names[k].table,keyname,pattern,message);
  1292.         return;
  1293.     }
  1294.     }
  1295.  
  1296.     /* If we get here, then the pattern name is not in the built-in list,
  1297.        so create a new entry in pattern_names[] if space remains */
  1298.  
  1299.     if (k >= (sizeof(pattern_names)/sizeof(pattern_names[0]) - 1))
  1300.     {                    /* too many pattern types */
  1301.     (void)fprintf(stdlog,
  1302.         "%s Out of memory for pattern name [%s] -- pattern ignored\n",
  1303.         WARNING_PREFIX, keyname);
  1304.     }
  1305.     else
  1306.     {                    /* sufficient table space remains */
  1307.     pattern_names[k].name = strdup(keyname); /* add new table entry */
  1308.     pattern_names[k].table = (PATTERN_TABLE*)malloc(sizeof(PATTERN_TABLE));
  1309.     if (pattern_names[k].table == (PATTERN_TABLE*)NULL)
  1310.         fatal("Out of memory for pattern tables");
  1311.     pattern_names[k].table->patterns = (MATCH_PATTERN*)NULL;
  1312.     pattern_names[k].table->current_size = 0;
  1313.     pattern_names[k].table->maximum_size = 0;
  1314.  
  1315.     add_one_pattern(pattern_names[k].table,keyname,pattern,message);
  1316.  
  1317.     pattern_names[k+1].name = (char*)NULL; /* mark new end of table */
  1318.     pattern_names[k+1].table = (PATTERN_TABLE*)NULL;
  1319.     }
  1320. }
  1321.  
  1322.  
  1323. #if NEW_STYLE
  1324. static YESorNO
  1325. apply_function(const char *key, KEY_FUNCTION_ENTRY table[])
  1326. #else /* K&R style */
  1327. static YESorNO
  1328. apply_function(key,table)
  1329. const char *key;
  1330. KEY_FUNCTION_ENTRY table[];
  1331. #endif /* NEW_STYLE */
  1332. {    /* return YES if function matching key was invoked, otherwise NO */
  1333.     int k;            /* index into table[] */
  1334.  
  1335.     for (k = 0; table[k].name != (const char*)NULL; ++k)
  1336.     {
  1337.         if (strnicmp(key,table[k].name,table[k].min_match) == 0)
  1338.     {
  1339.         table[k].function();
  1340.         return (YES);
  1341.     }
  1342.     }
  1343.     return (NO);
  1344. }
  1345.  
  1346.  
  1347. #if NEW_STYLE
  1348. static void
  1349. bad_ISBN(char ISBN[11])
  1350. #else /* K&R style */
  1351. static void
  1352. bad_ISBN(ISBN)
  1353. char ISBN[11];
  1354. #endif /* NEW_STYLE */
  1355. {
  1356.     static char fmt[] =
  1357.     "Invalid checksum for ISBN %c-%c%c%c%c%c-%c%c%c-%c in ``%%k = %%v''";
  1358.     char msg[sizeof(fmt)];
  1359.  
  1360.     (void)sprintf(msg, fmt,
  1361.     (int)ISBN[1], (int)ISBN[2], (int)ISBN[3], (int)ISBN[4], (int)ISBN[5],
  1362.     (int)ISBN[6], (int)ISBN[7], (int)ISBN[8], (int)ISBN[9], (int)ISBN[10]);
  1363.     error(msg);
  1364. }
  1365.  
  1366.  
  1367. #if NEW_STYLE
  1368. static void
  1369. bad_ISSN(char ISSN[9])
  1370. #else /* K&R style */
  1371. static void
  1372. bad_ISSN(ISSN)
  1373. char ISSN[9];
  1374. #endif /* NEW_STYLE */
  1375. {
  1376.     static char fmt[] =
  1377.     "Invalid checksum for ISSN %c%c%c%c-%c%c%c%c in ``%%k = %%v''";
  1378.     char msg[sizeof(fmt)];
  1379.  
  1380.     (void)sprintf(msg, fmt,
  1381.     (int)ISSN[1], (int)ISSN[2], (int)ISSN[3], (int)ISSN[4],
  1382.     (int)ISSN[5], (int)ISSN[6], (int)ISSN[7], (int)ISSN[8]);
  1383.     error(msg);
  1384. }
  1385.  
  1386.  
  1387. static void
  1388. check_chapter(VOID)
  1389. {
  1390. #if defined(HAVE_OLDCODE)
  1391.     size_t k;
  1392.     size_t n = strlen(current_value) - 1;
  1393.  
  1394.     /* match patterns like "23" and "23-1" */
  1395.     for (k = 1; k < n; ++k)
  1396.     {    /* omit first and last characters -- they are quotation marks */
  1397.     if (!(isdigit(current_value[k]) || (current_value[k] == '-')))
  1398.         break;
  1399.     }
  1400.     if (k == n)
  1401.     return;
  1402. #else /* NOT defined(HAVE_OLDCODE) */
  1403.     if (check_patterns(&pt_chapter,current_value) == YES)
  1404.     return;
  1405. #endif /* defined(HAVE_OLDCODE) */
  1406.  
  1407.     unexpected();
  1408. }
  1409.  
  1410.  
  1411. static void
  1412. check_inodes(VOID)
  1413. {
  1414.     struct stat buflog;
  1415.     struct stat bufout;
  1416.  
  1417.     stdlog_on_stdout = YES;            /* assume the worst initially */
  1418.  
  1419.     (void)fstat(fileno(stdlog),&buflog);
  1420.     (void)fstat(fileno(stdout),&bufout);
  1421.  
  1422. #if OS_UNIX
  1423.     stdlog_on_stdout = (buflog.st_ino == bufout.st_ino) ? YES : NO;
  1424. #endif /* OS_UNIX */
  1425.  
  1426. #if OS_PCDOS
  1427.     /* No inodes, so use creation times instead */
  1428.     stdlog_on_stdout = (buflog.st_ctime == bufout.st_ctime) ? YES : NO;
  1429. #endif /* OS_PCDOS */
  1430.  
  1431. #if OS_VAXVMS
  1432.     /* Inode field is 3 separate values */
  1433.     stdlog_on_stdout = ((buflog.st_ino[0] == bufout.st_ino[0]) &&
  1434.             (buflog.st_ino[1] == bufout.st_ino[1]) &&
  1435.             (buflog.st_ino[2] == bufout.st_ino[2])) ? YES : NO;
  1436. #endif /* OS_VAXVMS */
  1437.  
  1438. }
  1439.  
  1440.  
  1441. static void
  1442. check_ISBN(VOID)
  1443. {
  1444.     int checksum;
  1445.     char ISBN[11];            /* saved ISBN for error messages */
  1446.                     /* (use slots 1..10 instead of 0..9) */
  1447.     int k;                /* index into ISBN[] */
  1448.     size_t n;                /* index into current_value[] */
  1449.     YESorNO new_ISBN;            /* YES: start new ISBN */
  1450.  
  1451.     /*******************************************************************
  1452.        ISBN numbers are 10-character values from the set [0-9Xx], with
  1453.        a checksum given by
  1454.  
  1455.         (sum(k=1:9) digit(k) * k) mod 11 == digit(10)
  1456.  
  1457.        where digits have their normal value, X (or x) as a digit has
  1458.        value 10, and spaces and hyphens are ignored.  The sum is
  1459.        bounded from above by 10*(1 + 2 + ... + 9) = 450, so even short
  1460.        (16-bit) integers are sufficient for the accumulation.
  1461.  
  1462.        We allow multiple ISBN numbers separated by arbitrary
  1463.        characters other than [0-9Xx], and check each one of them.
  1464.     *******************************************************************/
  1465.  
  1466.     for (checksum = 0, k = 0, new_ISBN = YES, n = 1; current_value[n+1]; ++n)
  1467.     {                /* loop skips surrounding quotes */
  1468.     if (new_ISBN == YES)
  1469.     {
  1470.         (void)strcpy(ISBN,"???????????");
  1471.                     /* initialize for error messages */
  1472.         checksum = 0;    /* new checksum starting */
  1473.         k = 0;        /* no digits collected yet */
  1474.         new_ISBN = NO;    /* initialization done */
  1475.     }
  1476.     switch (current_value[n])
  1477.     {
  1478.     case ' ':
  1479.     case '-':
  1480.         break;        /* ignore space and hyphen */
  1481.  
  1482.     case '0':
  1483.     case '1':
  1484.     case '2':
  1485.     case '3':
  1486.     case '4':
  1487.     case '5':
  1488.     case '6':
  1489.     case '7':
  1490.     case '8':
  1491.     case '9':
  1492.     case 'X':
  1493.     case 'x':        /* valid ISBN digit */
  1494.         k++;
  1495.         if (k < 10)
  1496.         {
  1497.         ISBN[k] = current_value[n];
  1498.         checksum += ISBN_DIGIT_VALUE(ISBN[k]) * k;
  1499.         break;
  1500.         }
  1501.         else if (k == 10)
  1502.         {
  1503.         ISBN[k] = current_value[n];
  1504.         if ((checksum % 11) != ISBN_DIGIT_VALUE(ISBN[k]))
  1505.             bad_ISBN(ISBN);
  1506.         new_ISBN = YES;
  1507.         break;
  1508.         }
  1509.         /* k > 10: FALL THROUGH for error */
  1510.  
  1511.     default:        /* ignore all other characters */
  1512.         if (k > 0)        /* then only got partial ISBN */
  1513.         {
  1514.         bad_ISBN(ISBN);
  1515.         new_ISBN = YES;    /* start new checksum */
  1516.         }
  1517.         break;
  1518.     }            /* end switch (current_value[n]) */
  1519.     }                /* end for (loop over current_value[]) */
  1520.  
  1521.     if ((k > 0) && (new_ISBN == NO))    /* too few digits in last ISBN */
  1522.     bad_ISBN(ISBN);
  1523. }
  1524.  
  1525.  
  1526. static void
  1527. check_ISSN(VOID)
  1528. {
  1529.     int checksum;
  1530.     char ISSN[9];            /* saved ISSN for error messages */
  1531.                     /* (use slots 1..8 instead of 0..7) */
  1532.     int k;                /* index into ISSN[] */
  1533.     size_t n;                /* index into current_value[] */
  1534.     YESorNO new_ISSN;            /* YES: start new ISSN */
  1535.  
  1536.     /*******************************************************************
  1537.        ISSN numbers are 10-character values from the set [0-9Xx], with
  1538.        a checksum given by
  1539.  
  1540.         (sum(k=1:7) digit(k) * (k+2)) mod 11 == digit(8)
  1541.  
  1542.        where digits have their normal value, X (or x) as a digit has
  1543.        value 10, and spaces and hyphens are ignored.  The sum is
  1544.        bounded from above by 10*(3 + 4 + ... + 9) = 420, so even short
  1545.        (16-bit) integers are sufficient for the accumulation.
  1546.  
  1547.        We allow multiple ISSN numbers separated by arbitrary
  1548.        characters other than [0-9Xx], and check each one of them.
  1549.     *******************************************************************/
  1550.  
  1551.     for (checksum = 0, k = 0, new_ISSN = YES, n = 1; current_value[n+1]; ++n)
  1552.     {                    /* loop skips surrounding quotes */
  1553.     if (new_ISSN == YES)
  1554.     {
  1555.         (void)strcpy(ISSN,"?????????"); /* initialize for error messages */
  1556.         k = 0;        /* no digits collected yet */
  1557.         checksum = 0;    /* new checksum starting */
  1558.         new_ISSN = NO;    /* initialization done */
  1559.     }
  1560.     switch (current_value[n])
  1561.     {
  1562.     case ' ':
  1563.     case '-':
  1564.         break;        /* ignore space and hyphen */
  1565.  
  1566.     case '0':
  1567.     case '1':
  1568.     case '2':
  1569.     case '3':
  1570.     case '4':
  1571.     case '5':
  1572.     case '6':
  1573.     case '7':
  1574.     case '8':
  1575.     case '9':
  1576.     case 'X':
  1577.     case 'x':        /* valid ISSN digit */
  1578.         k++;
  1579.         if (k < 8)
  1580.         {
  1581.         ISSN[k] = current_value[n];
  1582.         checksum += ISSN_DIGIT_VALUE(ISSN[k]) * (k + 2);
  1583.         break;
  1584.         }
  1585.         else if (k == 8)
  1586.         {
  1587.         ISSN[k] = current_value[n];
  1588.         if ((checksum % 11) != ISSN_DIGIT_VALUE(ISSN[k]))
  1589.             bad_ISSN(ISSN);
  1590.         new_ISSN = YES;
  1591.         break;
  1592.         }
  1593.         /* k > 8: FALL THROUGH for error */
  1594.  
  1595.     default:        /* ignore all other characters */
  1596.         if (k > 0)        /* then only got partial ISSN */
  1597.         {
  1598.         bad_ISSN(ISSN);
  1599.         new_ISSN = YES;    /* start new checksum */
  1600.         }
  1601.         break;
  1602.     }            /* end switch (current_value[n]) */
  1603.     }                /* end for (loop over current_value[]) */
  1604.  
  1605.     if ((k > 0) && (new_ISSN == NO))    /* too few digits in last ISSN */
  1606.     bad_ISSN(ISSN);
  1607. }
  1608.  
  1609.  
  1610. #if NEW_STYLE
  1611. static void
  1612. check_length(size_t n)
  1613. #else /* K&R style */
  1614. static void
  1615. check_length(n)
  1616. size_t n;
  1617. #endif /* NEW_STYLE */
  1618. {
  1619.     if ((check_values == YES) && (n >= STD_MAX_TOKEN))
  1620.     warning("String length exceeds standard BibTeX limit for ``%k'' entry");
  1621. }
  1622.  
  1623.  
  1624. static void
  1625. check_month(VOID)
  1626. {
  1627.     int m;            /* month index */
  1628.     size_t n = strlen(current_value);
  1629.  
  1630.     if (STREQUAL(current_value,"\"\"")) /* ignore empty values */
  1631.     return;
  1632.  
  1633.     if (n == 3)            /* check for match against standard abbrevs */
  1634.     {
  1635.     for (m = 0; m < 12; ++m)
  1636.     {
  1637.         if (strnicmp(month_pair[m].new_name,current_value,3) == 0)
  1638.         return;
  1639.     }
  1640.     }
  1641.  
  1642.     /* Hand coding for the remaining patterns is too ugly to contemplate,
  1643.        so we only provide the checking when real pattern matching is
  1644.        available. */
  1645.  
  1646. #if !defined(HAVE_OLDCODE)
  1647.     if (check_patterns(&pt_month,current_value) == YES)
  1648.     return;
  1649. #endif /* !defined(HAVE_OLDCODE) */
  1650.  
  1651.     unexpected();
  1652. }
  1653.  
  1654.  
  1655. static void
  1656. check_number(VOID)
  1657. {
  1658. #if defined(HAVE_OLDCODE)
  1659.     size_t k;
  1660.     size_t n = strlen(current_value) - 1;
  1661.  
  1662.     if (STREQUAL(current_value,"\"\"")) /* ignore empty values */
  1663.     return;
  1664.  
  1665.     /* We expect the value string to match the regexp "[0-9a-zA-Z---,/ ()]+
  1666.     to handle values like "UMIACS-TR-89-11, CS-TR-2189, SRC-TR-89-13",
  1667.     "RJ 3847 (43914)", "{STAN-CS-89-1256}", "UMIACS-TR-89-3.1, CS-TR-2177.1",
  1668.     "TR\#89-24", "23", "23-27", and "3+4". */
  1669.  
  1670.     for (k = 1; k < n; ++k)
  1671.     {    /* omit first and last characters -- they are quotation marks */
  1672.     if (!(     isalnum(current_value[k])
  1673.         || isspace(current_value[k]) || (current_value[k] == '-')
  1674.         || (current_value[k] == '+') || (current_value[k] == ',')
  1675.         || (current_value[k] == '.') || (current_value[k] == '/')
  1676.         || (current_value[k] == '#') || (current_value[k] == '\\')
  1677.         || (current_value[k] == '(') || (current_value[k] == ')')
  1678.         || (current_value[k] == '{') || (current_value[k] == '}') ))
  1679.         break;
  1680.     }
  1681.     if (k == n)
  1682.     return;
  1683. #else /* NOT defined(HAVE_OLDCODE) */
  1684.     if (check_patterns(&pt_number,current_value) == YES)
  1685.     return;
  1686. #endif /* defined(HAVE_OLDCODE) */
  1687.  
  1688.     unexpected();
  1689. }
  1690.  
  1691.  
  1692. static void
  1693. check_other(VOID)
  1694. {
  1695.     int k;                /* index into pattern_names[] */
  1696.     size_t n = strlen(current_key);
  1697.  
  1698.     for (k = 0; pattern_names[k].name != (const char*)NULL; ++k)
  1699.     {
  1700.     if (strnicmp(pattern_names[k].name,current_key,n) == 0)
  1701.     {                /* then found the required table */
  1702.         if (check_patterns(pattern_names[k].table,current_value) == NO)
  1703.         unexpected();
  1704.         return;
  1705.     }
  1706.     }
  1707. }
  1708.  
  1709.  
  1710. static void
  1711. check_pages(VOID)
  1712. {
  1713.     /* Need to handle "B721--B729" as well as "721--729"; some
  1714.        physics journals use an initial letter in page number. */
  1715.  
  1716. #if defined(HAVE_OLDCODE)
  1717.     int number = 1;
  1718.     size_t k;
  1719.     size_t n = strlen(current_value) - 1;
  1720.  
  1721.     if (STREQUAL(current_value,"\"\"")) /* ignore empty values */
  1722.     return;
  1723.  
  1724.     /* We expect the value string to match the regexps [0-9]+ or
  1725.        [0-9]+--[0-9]+ */
  1726.     for (k = 1; k < n; ++k)
  1727.     {    /* omit first and last characters -- they are quotation marks */
  1728.     switch (current_value[k])
  1729.     {
  1730.     case '0':
  1731.     case '1':
  1732.     case '2':
  1733.     case '3':
  1734.     case '4':
  1735.     case '5':
  1736.     case '6':
  1737.     case '7':
  1738.     case '8':
  1739.     case '9':
  1740.         if (number > 2)
  1741.         {
  1742.         warning("More than 2 page numbers in ``%k = %v''");
  1743.         return;
  1744.         }
  1745.         break;
  1746.  
  1747.     case '-':
  1748.         number++;
  1749.         if (current_value[k+1] != '-')    /* expect -- */
  1750.         {
  1751.         warning(
  1752.             "Use en-dash, --, to separate page numbers in ``%k = %v''");
  1753.         return;
  1754.         }
  1755.         ++k;
  1756.         if (current_value[k+1] == '-')    /* should not have --- */
  1757.         {
  1758.         warning(
  1759.             "Use en-dash, --, to separate page numbers in ``%k = %v''");
  1760.         return;
  1761.         }
  1762.         break;
  1763.  
  1764.     case ',':
  1765.         number++;
  1766.         break;
  1767.  
  1768.     default:
  1769.         unexpected();
  1770.         return;
  1771.     }
  1772.     }
  1773. #else /* NOT defined(HAVE_OLDCODE) */
  1774.     if (check_patterns(&pt_pages,current_value) == YES)
  1775.     return;
  1776. #endif /* defined(HAVE_OLDCODE) */
  1777.  
  1778.     unexpected();
  1779. }
  1780.  
  1781.  
  1782. #if (defined(HAVE_PATTERNS) || defined(HAVE_REGEXP) || defined(HAVE_RECOMP))
  1783.  
  1784. #if NEW_STYLE
  1785. static YESorNO
  1786. check_patterns(PATTERN_TABLE* pt,const char *value)
  1787. #else /* K&R style */
  1788. static YESorNO
  1789. check_patterns(pt,value)
  1790. PATTERN_TABLE* pt;
  1791. const char *value;
  1792. #endif /* NEW_STYLE */
  1793. {
  1794.     /* Return YES if current_value[] matches a pattern, or there are no
  1795.        patterns, and NO if there is a match failure.  Any message
  1796.        associated with a successfully-matched pattern is printed before
  1797.        returning. */
  1798.  
  1799.     int k;
  1800.  
  1801.     for (k = 0; k < pt->current_size; ++k)
  1802.     {
  1803.     if (PATTERN_MATCHES(value,pt->patterns[k].pattern))
  1804.     {
  1805.         if (pt->patterns[k].message != (const char*)NULL)
  1806.         {
  1807.         if (pt->patterns[k].message[0] == '?') /* special error flag */
  1808.             error(pt->patterns[k].message + 1);
  1809.         else        /* just normal warning */
  1810.             warning(pt->patterns[k].message);
  1811.         }
  1812.         return (YES);
  1813.     }
  1814.     }
  1815.     return ((pt->current_size == 0) ? YES : NO);
  1816. }
  1817. #endif /* (defined(HAVE_PATTERNS) || defined(HAVE_REGEXP) ||
  1818.        defined(HAVE_RECOMP)) */
  1819.  
  1820.  
  1821. static void
  1822. check_tag(VOID)
  1823. {
  1824.     int k;                /* index into pattern_names[] */
  1825.     size_t n = strlen(current_tag);
  1826.  
  1827.     for (k = 0; pattern_names[k].name != (const char*)NULL; ++k)
  1828.     {
  1829.     if (strnicmp(pattern_names[k].name,current_tag,n) == 0)
  1830.     {                /* then found the required table */
  1831.         if (check_patterns(pattern_names[k].table,current_tag) == NO)
  1832.         warning("Unexpected citation tag ``%t''");
  1833.         return;
  1834.     }
  1835.     }
  1836. }
  1837.  
  1838.  
  1839. static void
  1840. check_volume(VOID)
  1841. {
  1842. #if defined(HAVE_OLDCODE)
  1843.     size_t k;
  1844.     size_t n = strlen(current_value) - 1;
  1845.  
  1846.     if (STREQUAL(current_value,"\"\"")) /* ignore empty values */
  1847.     return;
  1848.  
  1849.     /* Match patterns like "27", "27A", "27/3", "27A 3", "SMC-13", "VIII",
  1850.        "B", "{IX}", "1.2", "Special issue A", and  "11 and 12".  However,
  1851.        NEVER match pattern like "11(5)", since that is probably an erroneous
  1852.        incorporation of issue number into the volume value. */
  1853.  
  1854.     for (k = 1; k < n; ++k)
  1855.     {    /* omit first and last characters -- they are quotation marks */
  1856.     if (!(     isalnum(current_value[k])
  1857.         || (current_value[k] == '-')
  1858.         || (current_value[k] == '/')
  1859.         || (current_value[k] == '.')
  1860.         || (current_value[k] == ' ')
  1861.         || (current_value[k] == '{')
  1862.         || (current_value[k] == '}') ))
  1863.     {
  1864.         unexpected();
  1865.         return;
  1866.     }
  1867.     }
  1868. #else /* NOT defined(HAVE_OLDCODE) */
  1869.     if (check_patterns(&pt_volume,current_value) == YES)
  1870.     return;
  1871. #endif /* defined(HAVE_OLDCODE) */
  1872.  
  1873.     unexpected();
  1874. }
  1875.  
  1876.  
  1877. static void
  1878. check_year(VOID)
  1879. {
  1880.     char *p;
  1881.     char *q;
  1882.     long year;
  1883.  
  1884. #if defined(HAVE_OLDCODE)
  1885.     size_t k;
  1886.     size_t n;
  1887.  
  1888.     if (STREQUAL(current_value,"\"\"")) /* ignore empty values */
  1889.     return;
  1890.  
  1891.     /* We expect the value string to match the regexp [0-9]+ */
  1892.     for (k = 1, n = strlen(current_value) - 1; k < n; ++k)
  1893.     {    /* omit first and last characters -- they are quotation marks */
  1894.     if (!isdigit(current_value[k]))
  1895.     {
  1896.         warning("Non-digit found in value field of ``%k = %v''");
  1897.         return;
  1898.     }
  1899.     }
  1900. #else /* NOT defined(HAVE_PATTERNS) */
  1901.     if (check_patterns(&pt_year,current_value) == YES)
  1902.     return;
  1903.     unexpected();
  1904. #endif /* defined(HAVE_PATTERNS) */
  1905.  
  1906.     for (p = current_value; *p ; ) /* now validate all digit strings */
  1907.     {
  1908.     if (isdigit(*p))    /* then have digit string */
  1909.     {            /* now make sure year is `reasonable' */
  1910.         year = strtol(p,&q,10);
  1911.         if ((year < 1800L) || (year > 2099L))
  1912.             warning("Suspicious year in ``%k = %v''");
  1913.         p = q;
  1914.     }
  1915.     else            /* ignore other characters */
  1916.         p++;
  1917.     }
  1918. }
  1919.  
  1920.  
  1921. #if NEW_STYLE
  1922. static void
  1923. do_args(int argc, char *argv[])
  1924. #else /* K&R style */
  1925. static void
  1926. do_args(argc,argv)
  1927. int argc;
  1928. char *argv[];
  1929. #endif /* NEW_STYLE */
  1930. {
  1931.     int k;
  1932.     int nfiles;
  1933.  
  1934.     static KEY_FUNCTION_ENTRY options[] =
  1935.     {
  1936.     {"?",                1,    opt_help},
  1937.     {"author",            1,    opt_author},
  1938.     {"check-values",        1,    opt_check_values},
  1939.     {"delete-empty-fields",        1,    opt_delete_empty_fields},
  1940.     {"error-log",            1,    opt_error_log},
  1941.     {"file-position",        3,    opt_file_position},
  1942.     {"fix-initials",        5,    opt_fix_initials},
  1943.     {"fix-names",            5,    opt_fix_names},
  1944.     {"help",            1,    opt_help},
  1945.     {"init-file",            1,    opt_init_file},
  1946.     {"no-check-values",        4,    opt_check_values},
  1947.     {"no-delete-empty-fields",    4,    opt_delete_empty_fields},
  1948.     {"no-file-position",        6,    opt_file_position},
  1949.     {"no-fix-initials",        8,    opt_fix_initials},
  1950.     {"no-fix-names",        8,    opt_fix_names},
  1951.     {"no-parbreaks",        5,    opt_parbreaks},
  1952.     {"no-print-patterns",        5,    opt_print_patterns},
  1953.     {"no-read-init-files",        6,    opt_read_init_files},
  1954.     {"no-remove-OPT-prefixes",    6,    opt_remove_OPT_prefixes},
  1955.     {"no-scribe",            4,    opt_scribe},
  1956.     {"no-trace-file-opening",    4,    opt_trace_file_opening},
  1957.     {"no-warnings",            4,    opt_warnings},
  1958.     {"parbreaks",            2,    opt_parbreaks},
  1959.     {"print-patterns",        2,    opt_print_patterns},
  1960.     {"read-init-files",        3,    opt_read_init_files},
  1961.     {"remove-OPT-prefixes",        3,    opt_remove_OPT_prefixes},
  1962.     {"scribe",            1,    opt_scribe},
  1963.     {"trace-file-opening",        1,    opt_trace_file_opening},
  1964.     {"version",            1,    opt_version},
  1965.     {"warnings",            1,    opt_warnings},
  1966.     {(const char*)NULL,        0,    (void (*)(VOID))NULL},
  1967.     };
  1968.  
  1969.     for (nfiles = 1, k = 1; k < argc; ++k)
  1970.     {
  1971.     if ( (argv[k][1] != '\0') && isoptionprefix(argv[k][0]) )
  1972.     {                /* then process command-line switch */
  1973.         current_index = k;        /* needed by opt_init_file() and */
  1974.         next_option = argv[k+1];    /* opt_error_log() */
  1975.         current_option = argv[k];    /* needed by YESorNOarg() */
  1976.         if (apply_function(current_option+1,options) == NO)
  1977.         {
  1978.         usage();
  1979.         exit(EXIT_FAILURE);
  1980.         }
  1981.         k = current_index;        /* some opt_xxx() functions update it */
  1982.     }
  1983.     else                /* save file names */
  1984.         argv[nfiles++] = argv[k];    /* shuffle file names down */
  1985.     }
  1986.     argv[nfiles] = (char*)NULL;        /* terminate new argument list */
  1987. }
  1988.  
  1989.  
  1990. static void
  1991. do_at(VOID)            /* parse @name{...} */
  1992. {
  1993.     int c;
  1994.  
  1995.     c = get_next_non_blank();
  1996.     the_entry = the_file;
  1997.     if ((non_white_chars == 1) && (c == '@'))
  1998.     {
  1999.     at_level++;
  2000.     out_c(c);
  2001.     if (brace_level != 0)
  2002.     {
  2003.         error(
  2004.     "@ begins line, but brace level is not zero after entry ``@%e{%t,''");
  2005.         brace_level = 0;
  2006.     }
  2007.     }
  2008.     else if (c != EOF)
  2009.     {
  2010.     out_c(c);
  2011.     out_with_error("", "Expected @name{...} after entry ``@%e{%t,''");
  2012.     }
  2013. }
  2014.  
  2015.  
  2016. static void
  2017. do_BibTeX_entry(VOID)
  2018. {
  2019.     /*************************************************************
  2020.      Parse a BibTeX entry, one of:
  2021.        @entry-name{tag,key=value,key=value,...,}
  2022.        @string{name=value}
  2023.        @preamble{...}
  2024.     *************************************************************/
  2025.  
  2026.     new_entry();
  2027.  
  2028.     do_at();
  2029.     if ((rflag == YES) || (eofile == YES)) return;
  2030.  
  2031.     do_entry_name();
  2032.     if (rflag == YES) return;
  2033.  
  2034.     if (STREQUAL(current_entry_name,"Preamble"))
  2035.     do_group();
  2036.     else if (STREQUAL(current_entry_name,"String"))
  2037.     do_group();
  2038.     else            /* expect @name{tag, key = value, ... } */
  2039.     {
  2040.     do_open_brace();
  2041.     if (rflag == YES) return;
  2042.  
  2043.     do_tag_name();
  2044.     if (rflag == YES) return;
  2045.  
  2046.     do_comma();
  2047.     if (rflag == YES) return;
  2048.  
  2049.     while (do_key_value_pair() == YES)
  2050.         NOOP;
  2051.     if (rflag == YES) return;
  2052.  
  2053.     do_close_brace();
  2054.     }
  2055.     flush_inter_entry_space();
  2056. }
  2057.  
  2058.  
  2059. /***********************************************************************
  2060. BibTeX value fields can take several forms, as illustrated by this
  2061. simple BNF grammar:
  2062.  
  2063. BibTeX-value-string:
  2064.     simple-string |
  2065.     simple-string # BibTeX-value-string
  2066.  
  2067. simple-string:
  2068.     "quoted string" |
  2069.     {braced-string} |
  2070.     digit-sequence    |
  2071.     alpha-sequence    |
  2072. ***********************************************************************/
  2073.  
  2074. static void
  2075. do_BibTeX_value(VOID)        /* process BibTeX value string */
  2076. {
  2077.     int c;
  2078.     size_t k;
  2079.     char *s;
  2080.     static char empty_string[] = "";
  2081.     size_t n;
  2082.  
  2083.     the_value = the_file;
  2084.     s = get_simple_string();
  2085.     for (k = 0; *s; )
  2086.     {
  2087.     n = strlen(s);
  2088.     if ((k + n) >= MAX_TOKEN)
  2089.     {
  2090.         current_value[k] = (char)'\0';
  2091.         out_s(current_value);
  2092.         out_with_error(s, "Value too long for key ``%k''");
  2093.         return;
  2094.     }
  2095.     (void)strcpy(¤t_value[k],s);
  2096.     k += n;
  2097.     c = get_next_non_blank();
  2098.     if ((parbreaks == NO) && (is_parbreak == YES))
  2099.     {
  2100.         APPEND_CHAR(current_value,k,c);
  2101.         out_with_parbreak_error(current_value);
  2102.         return;
  2103.     }
  2104.     if (c == '#')
  2105.     {
  2106.         if ((k + 3) >= MAX_TOKEN)
  2107.         {
  2108.         current_value[k] = (char)'\0';
  2109.         out_s(current_value);
  2110.         out_with_error(" # ", "Value too long for key ``%k''");
  2111.         return;
  2112.         }
  2113.         current_value[k++] = (char)' ';
  2114.         current_value[k++] = (char)'#';
  2115.         current_value[k++] = (char)' ';
  2116.         s = get_simple_string();
  2117.     }
  2118.     else            /* end of string */
  2119.     {
  2120.         put_back(c);
  2121.         s = empty_string;
  2122.     }
  2123.     }
  2124.     out_value();
  2125. }
  2126.  
  2127.  
  2128. static void
  2129. do_close_brace(VOID)    /* parse level 1 closing brace or parenthesis */
  2130. {
  2131.     int c;
  2132.  
  2133.     c = get_next_non_blank();
  2134.     if (c == EOF)
  2135.     return;
  2136.     else if (c == close_char)
  2137.     {
  2138.     if (c == ')')
  2139.         brace_level--;    /* get_char() could not do this for us */
  2140.     out_c('}');        /* standardize parenthesis to brace */
  2141.     if (brace_level != 0)
  2142.         out_with_error("",
  2143.     "Non-zero brace level after @name{...} processed.  Last tag = ``%t''");
  2144.     }
  2145.     else            /* raise error and try to resynchronize */
  2146.     {
  2147.     out_c(c);
  2148.     out_with_error("",
  2149.         "Expected closing brace or parenthesis in entry ``@%e{%t,''");
  2150.     }
  2151. }
  2152.  
  2153.  
  2154. static void
  2155. do_comma(VOID)
  2156. {
  2157.     int c;
  2158.  
  2159.     /* Parse a comma, or an optional comma before a closing brace or
  2160.        parenthesis;  an omitted legal comma is supplied explicitly.
  2161.        A newline is output after the comma so that key = value
  2162.        pairs appear on separate lines. */
  2163.  
  2164.     the_value = the_file;
  2165.  
  2166.     c = get_next_non_blank();
  2167.     if (c == EOF)
  2168.     NOOP;
  2169.     else if (c == ',')
  2170.     {
  2171.     if (discard_next_comma == NO)
  2172.         out_s(",\n");
  2173.     }
  2174.     else if (c == close_char)
  2175.     {            /* supply missing comma for last key = value pair*/
  2176.     if (c == ')')
  2177.         brace_level--;    /* get_char() could not do this for us */
  2178.     if (brace_level == 0)    /* reached end of bibliography entry */
  2179.     {
  2180.         if (c == ')')
  2181.         brace_level++;    /* put_back() could not do this for us */
  2182.         put_back(c);
  2183.         if (discard_next_comma == NO)
  2184.         out_s(",\n");
  2185.     }
  2186.     else            /* no comma, and still in bibliography entry */
  2187.     {
  2188.         out_c(c);
  2189.         out_with_error("","Non-zero brace level after @name{...} \
  2190. processed.  Last entry = ``@%e{%t,''");
  2191.     }
  2192.     }
  2193.     else            /* raise error and try to resynchronize */
  2194.     {
  2195.     out_c(c);
  2196.     out_with_error("", "Expected comma after last key ``%k''");
  2197.     }
  2198.     discard_next_comma = NO;
  2199. }
  2200.  
  2201.  
  2202. static void
  2203. do_entry_name(VOID)        /* process BibTeX entry name */
  2204. {
  2205.     int c;
  2206.     size_t k;
  2207.     int n;
  2208.     static NAME_PAIR entry_pair[] =
  2209.     {                    /* entry name case change table */
  2210.     { "Deathesis",        "DEAthesis" },
  2211.     { "Inbook",        "InBook" },
  2212.     { "Incollection",    "InCollection" },
  2213.     { "Inproceedings",    "InProceedings" },
  2214.     { "Mastersthesis",    "MastersThesis" },
  2215.     { "Phdthesis",        "PhdThesis" },
  2216.     { "Techreport",        "TechReport" },
  2217.     };
  2218.  
  2219.     for (k = 0; ((c = get_next_non_blank()) != EOF) && isidchar(c); ++k)
  2220.     {                /* store capitalized entry name */
  2221.     if ((k == 0) && !isalpha(c))
  2222.         error("Non-alphabetic character begins an entry name");
  2223.     if ((k == 0) && islower(c))
  2224.         c = toupper(c);
  2225.     else if ((k > 0) && isupper(c))
  2226.         c = tolower(c);
  2227.     if ((parbreaks == NO) && (is_parbreak == YES))
  2228.     {
  2229.         APPEND_CHAR(current_entry_name,k,c);
  2230.         out_with_parbreak_error(current_entry_name);
  2231.         return;
  2232.     }
  2233.     if (k >= MAX_TOKEN)
  2234.     {
  2235.         APPEND_CHAR(current_entry_name,k,c);
  2236.         out_with_error(current_entry_name, "@entry_name too long");
  2237.         return;
  2238.     }
  2239.     current_entry_name[k] = (char)c;
  2240.     }
  2241.     current_entry_name[k] = (char)'\0';
  2242.     if (c != EOF)
  2243.     put_back(c);
  2244.  
  2245.     /* Substitute a few keywords that look better in upper case */
  2246.     for (n = 0; n < (int)(sizeof(entry_pair)/sizeof(entry_pair[0])); ++n)
  2247.     if (STREQUAL(current_entry_name,entry_pair[n].old_name))
  2248.         (void)strcpy(current_entry_name,entry_pair[n].new_name);
  2249.  
  2250.     out_s(current_entry_name);
  2251.     check_length(k);
  2252. }
  2253.  
  2254.  
  2255. static void
  2256. do_equals(VOID)            /* process = in key = value */
  2257. {
  2258.     int c;
  2259.  
  2260.     the_value = the_file;
  2261.  
  2262.     c = get_next_non_blank();
  2263.     if (c == EOF)
  2264.     NOOP;
  2265.     else if (c == '=')
  2266.     out_equals();
  2267.     else
  2268.     {
  2269.     out_c(c);
  2270.     out_with_error("", "Expected \"=\" after key ``%k''");
  2271.     }
  2272.     out_spaces((int)(VALUE_INDENTATION - the_file.output.column_position));
  2273.                 /* supply leading indentation */
  2274. }
  2275.  
  2276.  
  2277. #if NEW_STYLE
  2278. static void
  2279. do_escapes(char *s)
  2280. #else /* K&R style */
  2281. static void
  2282. do_escapes(s)
  2283. char *s;
  2284. #endif /* NEW_STYLE */
  2285. {                    /* reduce escape sequences in s[] */
  2286.     int base;                /* number base for strtol() */
  2287.     char *endptr;            /* pointer returned by strtol() */
  2288.     char *p;                /* pointer into output s[] */
  2289.  
  2290.     if (s == (char*)NULL)        /* nothing to do if no string */
  2291.     return;
  2292.  
  2293.     for (p = s ; *s ; ++s)
  2294.     {
  2295.     if (*s == '\\')            /* have escaped character */
  2296.     {
  2297.         base = 8;            /* base is tentatively octal */
  2298.         switch (*++s)
  2299.         {
  2300.         case 'a':    *p++ = CTL('G'); break;
  2301.         case 'b':    *p++ = CTL('H'); break;
  2302.         case 'f':    *p++ = CTL('L'); break;
  2303.         case 'n':    *p++ = CTL('J'); break;
  2304.         case 'r':    *p++ = CTL('M'); break;
  2305.         case 't':    *p++ = CTL('I'); break;
  2306.         case 'v':    *p++ = CTL('K'); break;
  2307.  
  2308.         case '0':
  2309.         if (TOLOWER(s[1]) == 'x') /* 0x means hexadecimal */
  2310.             base = 16;
  2311.         /* FALL THROUGH */
  2312.         case '1':
  2313.         case '2':
  2314.         case '3':
  2315.         case '4':
  2316.         case '5':
  2317.         case '6':
  2318.         case '7':
  2319.         *p++ = (char)strtol((const char*)s,&endptr,base);
  2320.         s = endptr - 1;    /* point to last used character */
  2321.         break;
  2322.  
  2323.         default:            /* \x becomes x for all other x */
  2324.         *p++ = *s;
  2325.         break;
  2326.         }
  2327.     }
  2328.     else                /* not escaped, so just copy it */
  2329.         *p++ = *s;
  2330.     }
  2331.     *p = '\0';                /* terminate final string */
  2332. }
  2333.  
  2334.  
  2335. #if NEW_STYLE
  2336. static void
  2337. do_fileinit(const char *bibfilename)    /* process one initialization file */
  2338. #else /* K&R style */
  2339. static void
  2340. do_fileinit(bibfilename)        /* process one initialization file */
  2341. const char *bibfilename;
  2342. #endif /* NEW_STYLE */
  2343. {
  2344.     char *p;
  2345.  
  2346.     if (strrchr(bibfilename,'.') != (char*)NULL) /* then have file extension */
  2347.     {   /* convert foo.bib to foo.ini and then process it as an init file */
  2348.     if ((p = (char*)malloc(strlen(bibfilename) +
  2349.         sizeof(INITFILE_EXT) + 1)) != (char*)NULL)
  2350.     {
  2351.         (void)strcpy(p,bibfilename);
  2352.         (void)strcpy(strrchr(p,'.'),INITFILE_EXT);
  2353.         do_initfile((char*)NULL,p);
  2354.         free(p);
  2355.     }
  2356.     }
  2357. }
  2358.  
  2359.  
  2360. #if NEW_STYLE
  2361. static void
  2362. do_files(int argc, char *argv[])
  2363. #else /* K&R style */
  2364. static void
  2365. do_files(argc,argv)
  2366. int argc;
  2367. char *argv[];
  2368. #endif /* NEW_STYLE */
  2369. {
  2370.     FILE *fp;
  2371.     int k = argc;            /* index into argv[] */
  2372.     /* set to argc to remove optimizer complaints about unused argument */
  2373.  
  2374.     if (argv[1] == (char*)NULL)    /* no files specified, so use stdin */
  2375.     {
  2376.     the_file.input.filename = "stdin";
  2377.     do_one_file(stdin);
  2378.     }
  2379.     else            /* else use command-line files left in argv[] */
  2380.     {
  2381.     for (k = 1; argv[k] != (char*)NULL; ++k)
  2382.     {
  2383.         if (STREQUAL(argv[k],"-"))
  2384.         {
  2385.         /* A filename of "-" is conventionally interpreted in
  2386.            the UNIX world as a synonym for stdin, since that
  2387.            system otherwise lacks true filenames for stdin,
  2388.            stdout, and stdlog.    We process stdin with
  2389.            do_one_file(), but never close it so that subsequent
  2390.            read attempts will silently, and harmlessly, fail
  2391.            at end-of-file. */
  2392.         the_file.input.filename = "stdin";
  2393.         do_one_file(stdin);
  2394.         }
  2395.         else if ((fp = tfopen(argv[k], "r")) == (FILE*)NULL)
  2396.         (void)fprintf(stdlog,
  2397.             "\n%s Ignoring open failure on file [%s]\n",
  2398.             ERROR_PREFIX, argv[k]);
  2399.         else            /* open succeeded, so process file */
  2400.         {
  2401.         if (k > 1)        /* supply blank line between */
  2402.             out_c('\n');    /* entries at file boundaries */
  2403.         the_file.input.filename = argv[k];
  2404.         if (read_initialization_files == YES)
  2405.             do_fileinit(the_file.input.filename);
  2406.         do_one_file(fp);
  2407.         (void)fclose(fp);    /* close to save file resources */
  2408.         }
  2409.     }
  2410.     }
  2411. }
  2412.  
  2413.  
  2414. static void
  2415. do_group(VOID)            /* copy a braced group verbatim */
  2416. {
  2417.     int c;
  2418.  
  2419.     do_open_brace();
  2420.     if (rflag == YES) return;
  2421.  
  2422.     while ((c = get_char()) != EOF)
  2423.     {
  2424.     if ((brace_level == 1) && (close_char == ')') && (c == close_char))
  2425.     {            /* end of @keyword(...) */
  2426.         brace_level = 0;
  2427.         c = '}';
  2428.     }
  2429.     if ((non_white_chars == 1) && (c == '@'))
  2430.         error("@ begins line, but brace level is not zero after \
  2431. entry ``@%e{%t,''");
  2432.     out_c(c);
  2433.     if (brace_level == 0)
  2434.         break;
  2435.     }
  2436. }
  2437.  
  2438.  
  2439. #if NEW_STYLE
  2440. static void
  2441. do_initfile(const char *pathlist, const char *name)
  2442. #else /* K&R style */
  2443. static void
  2444. do_initfile(pathlist,name)
  2445. const char *pathlist;
  2446. const char *name;
  2447. #endif /* NEW_STYLE */
  2448. {
  2449.  
  2450.     FILE *fp;
  2451.     char *p;
  2452.  
  2453.     if ((initialization_file_name = findfile(pathlist,name)) == (char*)NULL)
  2454.     return;                /* silently ignore missing files */
  2455.  
  2456.     if ((fp = tfopen(initialization_file_name,"r")) == (FILE*)NULL)
  2457.     return;                /* silently ignore missing files */
  2458.  
  2459.     while ((p = get_line(fp)) != (char *)NULL)
  2460.     {                    /* process init file lines */
  2461.     SKIP_SPACE(p);
  2462.     if (isoptionprefix(*p))
  2463.         do_single_arg(p);        /* then expect -option [value] */
  2464.     else
  2465.         do_new_pattern(p);        /* else expect key = "value" */
  2466.     }
  2467.     (void)fclose(fp);
  2468. }
  2469.  
  2470.  
  2471. static void
  2472. do_key(VOID)            /* process BibTeX key name */
  2473. {
  2474.     int c;
  2475.     size_t k;
  2476.     int n;
  2477.     static NAME_PAIR key_pair[] =
  2478.     {                    /* keyword case change table */
  2479.     { "ansi-standard-number",    "ANSI-standard-number" },
  2480.     { "ieee-standard-number",    "IEEE-standard-number" },
  2481.     { "isbn",            "ISBN" },
  2482.     { "iso-standard-number",    "ISO-standard-number" },
  2483.     { "issn",            "ISSN" },
  2484.     { "lccn",            "LCCN" },
  2485.     };
  2486.  
  2487.     the_value = the_file;
  2488.  
  2489.     for (k = 0, c = get_next_non_blank(); (c != EOF) && isidchar(c);
  2490.     c = get_char(), k++)
  2491.     {
  2492.     if (k >= MAX_TOKEN)
  2493.     {
  2494.         APPEND_CHAR(current_key,k,c);
  2495.         out_with_error(current_key, "Entry keyword too long");
  2496.         return;
  2497.     }
  2498.     else if ((k == 0) && !isalpha(c))
  2499.         error("Non-alphabetic character begins a keyword");
  2500.     current_key[k] = (char)(isupper(c) ? tolower(c) : c);
  2501.     }
  2502.     if (c != EOF)
  2503.     put_back(c);
  2504.     current_key[k] = (char)'\0';
  2505.  
  2506.     /* Substitute a few keywords that look better in upper case */
  2507.     for (n = 0; n < (int)(sizeof(key_pair)/sizeof(key_pair[0])); ++n)
  2508.     if (STREQUAL(current_key,key_pair[n].old_name))
  2509.         (void)strcpy(current_key,key_pair[n].new_name);
  2510.  
  2511.     if (strncmp("opt",current_key,3) == 0) /* GNU Emacs bibtex.el expects OPT*/
  2512.     (void)strncpy(current_key,"OPT",3);
  2513.  
  2514.     if (k > 0)
  2515.     out_key();
  2516.     check_length(k);
  2517. }
  2518.  
  2519.  
  2520. static YESorNO
  2521. do_key_value_pair(VOID)        /* process key = value pair */
  2522. {
  2523.     if (eofile == YES) return (NO);
  2524.  
  2525.     do_key();
  2526.     if ((rflag == YES) || (eofile == YES) || (current_key[0] == '\0'))
  2527.     return (NO);
  2528.  
  2529.     if (Scribe == YES)
  2530.     do_Scribe_separator();
  2531.     else
  2532.     do_equals();
  2533.     if ((rflag == YES) || (eofile == YES)) return (NO);
  2534.  
  2535.     if (Scribe == YES)
  2536.     do_Scribe_value();
  2537.     else
  2538.     do_BibTeX_value();
  2539.     if ((rflag == YES) || (eofile == YES)) return (NO);
  2540.  
  2541.     do_comma();            /* this supplies any missing optional comma */
  2542.     if ((rflag == YES) || (eofile == YES)) return (NO);
  2543.  
  2544.     return (YES);
  2545. }
  2546.  
  2547.  
  2548. #if (SCREEN_LINES > 0)
  2549. #if NEW_STYLE
  2550. static int
  2551. do_more(FILE *fpout,int line_number, int pause_after)
  2552. #else /* K&R style */
  2553. static int
  2554. do_more(fpout, line_number, pause_after)
  2555. FILE *fpout;
  2556. int line_number;
  2557. int pause_after;
  2558. #endif /* NEW_STYLE */
  2559. {
  2560.  
  2561. #if OS_PCDOS
  2562. #define MORE_HELP \
  2563. "More? f)orward b)ackward e)nd q)uit r)efresh t)op \030 \031 PgUp PgDn Home \
  2564. End\n\r"
  2565. #else /* NOT OS_PCDOS */
  2566. #define MORE_HELP \
  2567. "More? f)orward b)ackward d)own e)nd q)uit r)efresh t)op u)p\n\r"
  2568. #endif /* OS_PCDOS */
  2569.  
  2570.     (void)fputs(MORE_HELP,fpout);
  2571.     (void)fflush(fpout);        /* make screen up-to-date */
  2572.     for (;;)            /* loop until a valid input code is received */
  2573.     {
  2574.     switch (kbcode())
  2575.     {
  2576.     case KEY_PGUP:            /* backward screen */
  2577.         return (MAX(0,line_number + 1 - 2*pause_after));
  2578.  
  2579.     case KEY_DOWN:            /* go down 1 line (scroll up 1 line) */
  2580.         return (line_number + 2 - pause_after);
  2581.  
  2582.     case KEY_END:            /* end */
  2583.         return (LAST_SCREEN_LINE);
  2584.  
  2585.     case KEY_PGDN:            /* forward screen */
  2586.         return (line_number + 1);
  2587.  
  2588.     case KEY_HELP:
  2589.         (void)fputs(MORE_HELP,fpout);
  2590.         break;
  2591.  
  2592.     case KEY_EOF:
  2593.     case KEY_QUIT:
  2594.         return (EOF);
  2595.  
  2596.     case KEY_AGAIN:            /* refresh */
  2597.         return (MAX(0,line_number + 1 - pause_after));
  2598.  
  2599.     case KEY_HOME:            /* top */
  2600.         return (0);
  2601.  
  2602.     case KEY_UP:            /* go up 1 line (scroll down 1 line) */
  2603.         return (line_number + 0 - pause_after);
  2604.  
  2605.     case KEY_UNKNOWN:
  2606.     default:            /* anything else produces */
  2607.         fputc('\007',fpout);    /* an error beep */
  2608.         break;
  2609.     }                /* end switch (c...) */
  2610.     }                    /* end for (;;) */
  2611. }
  2612. #endif /* (SCREEN_LINES > 0) */
  2613.  
  2614.  
  2615. #if NEW_STYLE
  2616. static void
  2617. do_new_pattern(char *s)
  2618. #else /* K&R style */
  2619. static void
  2620. do_new_pattern(s)
  2621. char *s;
  2622. #endif /* NEW_STYLE */
  2623. {
  2624.     char *key;
  2625.     char *p = s;
  2626.     YESorNO saw_space;
  2627.     char *value;
  2628.  
  2629.     /*******************************************************************
  2630.       We expect s[] to contain
  2631.     key = "value"
  2632.     key : "value"
  2633.     key   "value"
  2634.     key = "value" "message"
  2635.     key : "value" "message"
  2636.     key   "value" "message"
  2637.       Empty lines are silently ignored.
  2638.     *******************************************************************/
  2639.  
  2640.     key = get_token(p,&p,"=: \t\v\f");
  2641.  
  2642.     if (key == (char*)NULL)
  2643.     return;                /* then we have an empty line */
  2644.  
  2645.     if (p != (char*)NULL)        /* then we have more text */
  2646.     {
  2647.     saw_space = isspace(*p) ? YES : NO;
  2648.     SKIP_SPACE(p);
  2649.     if (saw_space || iskeyvalueseparator(*p))
  2650.     {
  2651.         if (iskeyvalueseparator(*p))
  2652.         ++p;            /* then move past separator */
  2653.         SKIP_SPACE(p);
  2654.         if (*p == '"')        /* then have quoted value */
  2655.         {
  2656.         value = get_token(p,&p," \t\v\f");
  2657.         if (value != (char*)NULL)
  2658.         {
  2659.             SKIP_SPACE(p);
  2660.             if (*p == '"')    /* then have quoted message */
  2661.             {
  2662.             add_pattern(key,value,get_token(p,&p," \t\v\f"));
  2663.             return;
  2664.             }
  2665.             else if ((*p == '\0') || (*p == COMMENT_PREFIX))
  2666.             {            /* have end of string s[] */
  2667.             add_pattern(key,value,(char*)NULL);
  2668.             return;
  2669.             }
  2670.         }
  2671.         }
  2672.     }
  2673.     }
  2674.     (void)fprintf(stdlog,"%s Bad line [%s] in initialization file [%s]\n",
  2675.         ERROR_PREFIX, s, initialization_file_name);
  2676.     exit(EXIT_FAILURE);
  2677. }
  2678.  
  2679.  
  2680. #if NEW_STYLE
  2681. static void
  2682. do_one_file(FILE *fp)        /* process one input file on fp */
  2683. #else /* K&R style */
  2684. static void
  2685. do_one_file(fp)            /* process one input file on fp */
  2686. FILE *fp;
  2687. #endif /* NEW_STYLE */
  2688. {
  2689.     fpin = fp;            /* save file pointer globally for get_char() */
  2690.  
  2691.     new_io_pair(&the_file);
  2692.     eofile = NO;
  2693.     new_entry();
  2694.     while (eofile == NO)
  2695.     {
  2696.     do_other();
  2697.     if (Scribe == YES)
  2698.         do_Scribe_entry();
  2699.     else
  2700.         do_BibTeX_entry();
  2701.     }
  2702.     out_flush();        /* flush all buffered output */
  2703. }
  2704.  
  2705.  
  2706. static void
  2707. do_open_brace(VOID)        /* process open brace or parenthesis */
  2708. {
  2709.     int c;
  2710.  
  2711.     c = get_next_non_blank();
  2712.  
  2713.     if (c == EOF)
  2714.     return;
  2715.     else if (c == '{')
  2716.     {
  2717.     close_char = '}';
  2718.     out_c('{');
  2719.  
  2720.     }
  2721.     else if (c == '(')
  2722.     {
  2723.     close_char = ')';
  2724.     brace_level++;        /* get_char() could not do this for us */
  2725.     out_c('{');        /* standardize parenthesis to brace */
  2726.     }
  2727.     else            /* raise error and try to resynchronize */
  2728.     {
  2729.     out_c(c);
  2730.     out_with_error("",
  2731.         "Expected open brace or parenthesis.  Last entry = ``@%e{%t,''");
  2732.     }
  2733. }
  2734.  
  2735.  
  2736. static void
  2737. do_other(VOID)            /* copy non-BibTeX text verbatim */
  2738. {
  2739.     int c;
  2740.  
  2741.     while ((c = get_char()) != EOF)
  2742.     {
  2743.     if ((c == '@') && (non_white_chars == 1))
  2744.     {            /* new entry found */
  2745.         put_back(c);
  2746.         break;
  2747.     }
  2748.     out_c(c);
  2749.     }
  2750. }
  2751.  
  2752.  
  2753. #if NEW_STYLE
  2754. static void
  2755. do_preargs(int argc, char *argv[])
  2756. #else /* K&R style */
  2757. static void
  2758. do_preargs(argc,argv)
  2759. int argc;
  2760. char *argv[];
  2761. #endif /* NEW_STYLE */
  2762. {
  2763.     int k;
  2764.  
  2765.     static KEY_FUNCTION_ENTRY options[] =
  2766.     {
  2767.     {"no-print-patterns",        5,    opt_print_patterns},
  2768.     {"no-read-init-files",        6,    opt_read_init_files},
  2769.     {"no-trace-file-opening",    4,    opt_trace_file_opening},
  2770.     {"print-patterns",        2,    opt_print_patterns},
  2771.     {"read-init-files",        3,    opt_read_init_files},
  2772.     {"trace-file-opening",        1,    opt_trace_file_opening},
  2773.     {(const char*)NULL,        0,    (void (*)(VOID))NULL},
  2774.     };
  2775.  
  2776.     for (k = 1; k < argc; ++k)
  2777.     {
  2778.     /* Do argument scan for options that must be known BEFORE
  2779.     initializations are attempted. */
  2780.  
  2781.     if ( (argv[k][1] != '\0') && isoptionprefix(argv[k][0]) )
  2782.     {                /* then process command-line switch */
  2783.         current_index = k;
  2784.         current_option = argv[k];
  2785.         next_option = argv[k+1];
  2786.         (void)apply_function(current_option+1,options);
  2787.     }
  2788.     }
  2789. }
  2790.  
  2791.  
  2792. static void
  2793. do_Scribe_block_comment(VOID)
  2794. {
  2795.     int b_level = 0;            /* brace level */
  2796.     int c;
  2797.     int k;
  2798.     char *p;
  2799.     char s[3+1];            /* to hold "end" */
  2800.  
  2801.     p = get_Scribe_string();        /* expect to get "comment" */
  2802.  
  2803.     if (strnicmp(p,"\"comment\"",9) == 0)
  2804.     {                    /* found start of @Begin{comment} */
  2805.     for (k = 6; k > 0; --k)
  2806.         out_c(DELETE_CHAR);        /* delete "@Begin" from output */
  2807.                     /* that was output by do_entry_name() */
  2808.     out_s("@Comment{");        /* convert to BibTeX `comment' */
  2809.     while ((c = get_char()) != EOF)
  2810.     {
  2811.         switch (c)
  2812.         {
  2813.         case '@':            /* lookahead for "@End" */
  2814.         s[0] = (char)get_char();
  2815.         s[1] = (char)get_char();
  2816.         s[2] = (char)get_char();
  2817.         s[3] = (char)'\0';
  2818.         if (strnicmp(s,"end",3) == 0)
  2819.         {            /* then we have @End */
  2820.             p = get_Scribe_string(); /* so get what follows */
  2821.             if (strnicmp(p,"\"Comment\"",9) == 0)
  2822.             {
  2823.             out_c('}');    /* found @End{comment}, so finish
  2824.                        conversion to @Comment{...} */
  2825.             return;        /* block comment conversion done! */
  2826.             }
  2827.             else        /* false alarm, just stuff lookahead */
  2828.             {            /* back into input stream */
  2829.             put_back_string(p);
  2830.             put_back_string(s);
  2831.             }
  2832.         }
  2833.         else            /* lookahead was NOT "@End" */
  2834.             put_back_string(s);
  2835.         break;
  2836.         case '{':
  2837.         b_level++;
  2838.         break;
  2839.         case '}':
  2840.         if (b_level <= 0)
  2841.             out_c('{');        /* keep output braces balanced */
  2842.         else
  2843.             b_level--;
  2844.         break;
  2845.         }                /* end switch(c) */
  2846.         out_c(c);            /* copy one comment character */
  2847.     }                /* end while ((c = ...)) */
  2848.     }
  2849.     else                /* was not @Begin{comment} after all */
  2850.     put_back_string(p);
  2851. }
  2852.  
  2853.  
  2854. static void
  2855. do_Scribe_close_delimiter(VOID)
  2856. {
  2857.     int c;
  2858.     static char fmt[] = "Expected Scribe close delimiter `%c' [8#%03o], but \
  2859. found `%c' [8#%03o] instead for key ``%%k''";
  2860.     char msg[sizeof(fmt)];
  2861.  
  2862.     c = get_next_non_blank();
  2863.     if ((parbreaks == NO) && (is_parbreak == YES))
  2864.     {
  2865.     APPEND_CHAR(msg,0,c);
  2866.     out_with_parbreak_error(msg);
  2867.     return;
  2868.     }
  2869.     if (c == EOF)
  2870.     return;
  2871.     else if (c == close_char)
  2872.     out_c('}');        /* standardize parenthesis to brace */
  2873.     else            /* raise error and try to resynchronize */
  2874.     {
  2875.     out_c(c);
  2876.     (void)sprintf(msg, fmt, close_char, (unsigned int)close_char,
  2877.         (int)(isprint(c) ? c : '?'), (unsigned int)c);
  2878.     out_with_error("", msg);
  2879.     }
  2880. }
  2881.  
  2882.  
  2883. static void
  2884. do_Scribe_comment(VOID)
  2885. {
  2886.     int c;
  2887.     int b_level = 0;            /* brace level */
  2888.  
  2889.     /* BibTeX does not yet have a comment syntax, so we just output the
  2890.        Scribe comment in braces, ensuring that internal braces are balanced. */
  2891.  
  2892.     do_Scribe_open_delimiter();        /* this outputs an opening brace */
  2893.     if (rflag == YES) return;
  2894.  
  2895.     for (c = get_char(); (c != EOF) && (c != close_char); c = get_char())
  2896.     {
  2897.     if (c == '{')
  2898.         b_level++;
  2899.     else if (c == '}')
  2900.     {
  2901.         b_level--;
  2902.         if (b_level < 0)
  2903.         {
  2904.         out_c('{');        /* force matching internal braces */
  2905.         b_level++;
  2906.         }
  2907.     }
  2908.     out_c(c);
  2909.     }
  2910.     for (; b_level > 0; b_level--)
  2911.     out_c('}');            /* force matching internal braces */
  2912.  
  2913.     out_c('}');
  2914. }
  2915.  
  2916.  
  2917. static void
  2918. do_Scribe_entry(VOID)
  2919. {
  2920.     /*************************************************************
  2921.      Parse a Scribe entry, one of:
  2922.        @entry-name{tag,key=value,key=value,...,}
  2923.        @string{name=value}
  2924.        @comment{...}
  2925.        @begin{comment}...@end{comment}
  2926.      The = separator in key/value pairs may also be a space or
  2927.      a slash.
  2928.      Any of the seven Scribe delimiters can be used to surround
  2929.      the value(s) following @name, and to surround values of
  2930.      key value pairs.
  2931.     *************************************************************/
  2932.  
  2933.     int save_close_char;
  2934.  
  2935.     new_entry();
  2936.  
  2937.     do_at();
  2938.     if ((rflag == YES) || (eofile == YES)) return;
  2939.  
  2940.     do_entry_name();
  2941.     if (rflag == YES) return;
  2942.  
  2943.     if (STREQUAL(current_entry_name,"Comment"))
  2944.     do_Scribe_comment();
  2945.     else if (STREQUAL(current_entry_name,"Begin"))
  2946.     do_Scribe_block_comment();
  2947.     else if (STREQUAL(current_entry_name,"String"))
  2948.     do_group();
  2949.     else
  2950.     {
  2951.     do_Scribe_open_delimiter();
  2952.     if (rflag == YES) return;
  2953.     save_close_char = close_char;
  2954.  
  2955.     brace_level = 1;        /* get_char() cannot do this for us */
  2956.  
  2957.     do_tag_name();
  2958.     if (rflag == YES) return;
  2959.  
  2960.     do_comma();
  2961.     if (rflag == YES) return;
  2962.  
  2963.     while (do_key_value_pair() == YES)
  2964.         NOOP;
  2965.     if (rflag == YES) return;
  2966.  
  2967.     close_char = save_close_char;
  2968.     do_Scribe_close_delimiter();
  2969.     }
  2970.     flush_inter_entry_space();
  2971. }
  2972.  
  2973.  
  2974. static void
  2975. do_Scribe_open_delimiter(VOID)        /* process open delimiter */
  2976. {
  2977.     int c;
  2978.     char *p;
  2979.  
  2980.     c = get_next_non_blank();
  2981.  
  2982.     if (c == EOF)
  2983.     return;
  2984.     else
  2985.     {
  2986.     p = strchr(Scribe_open_delims,c);
  2987.     if (p == (char*)NULL)
  2988.     {
  2989.         out_c(c);
  2990.         out_with_error("",
  2991.     "Expected Scribe open delimiter, one of { [ ( < ' \" ` for key ``%k''");
  2992.         return;
  2993.     }
  2994.     close_char = Scribe_close_delims[(int)(p - Scribe_open_delims)];
  2995.     out_c('{');        /* standardize open delimiter to brace */
  2996.     }
  2997. }
  2998.  
  2999.  
  3000. static void
  3001. do_Scribe_separator(VOID)
  3002. {
  3003.     int c;
  3004.     YESorNO saw_space = NO;
  3005.  
  3006.     the_value = the_file;
  3007.  
  3008.     c = get_char();            /* need to look ahead one */
  3009.     put_back(c);            /* character in case separator */
  3010.     saw_space = isspace(c) ? YES : NO;    /* is just a space */
  3011.  
  3012.     c = get_next_non_blank();
  3013.     if ((parbreaks == NO) && (is_parbreak == YES))
  3014.     {
  3015.     char msg[2];
  3016.     APPEND_CHAR(msg,0,c);
  3017.     out_with_parbreak_error(msg);
  3018.     return;
  3019.     }
  3020.     if (c == EOF)
  3021.     NOOP;
  3022.     else if ((c == '=') || (c == '/'))
  3023.     out_equals();
  3024.     else if (saw_space == YES)    /* have key value with no binary operator */
  3025.     {
  3026.     out_equals();        /* supply the missing = operator */
  3027.     put_back(c);        /* this is first character of value string */
  3028.     }
  3029.     else            /* looks like run-together keyvalue */
  3030.     {
  3031.     out_c(c);
  3032.     out_with_error("",
  3033.         "Expected Scribe separator \"=\", \"/\", or \" \" for key ``%k''");
  3034.     }
  3035.     out_spaces((int)(VALUE_INDENTATION - the_file.output.column_position));
  3036.                 /* supply leading indentation */
  3037. }
  3038.  
  3039.  
  3040. /***********************************************************************
  3041. Scribe value fields can take several forms, as illustrated by this
  3042. simple BNF grammar:
  3043.  
  3044. Scribe-value-string:
  3045.     <open-delimiter><not-open-or-close-delimiter>*<close-delimiter> |
  3046.     <digit><letter-or-digit-or-dot>*
  3047.  
  3048. ***********************************************************************/
  3049.  
  3050. static void
  3051. do_Scribe_value(VOID)            /* process Scribe value string */
  3052. {
  3053.     the_value = the_file;
  3054.     (void)strcpy(current_value,get_Scribe_string());
  3055.     if ((rflag == YES) || (eofile == YES))
  3056.     out_s(current_value);
  3057.     else
  3058.     out_value();
  3059. }
  3060.  
  3061.  
  3062. #if NEW_STYLE
  3063. static void
  3064. do_single_arg(char *s)
  3065. #else /* K&R style */
  3066. static void
  3067. do_single_arg(s)
  3068. char *s;
  3069. #endif /* NEW_STYLE */
  3070. {                    /* expect -option or -option value */
  3071.     char *temp_argv[4];            /* "program" "-option" "value" NULL */
  3072.     int temp_argc;            /* temporary argument count */
  3073.  
  3074.     temp_argv[0] = program_name;    /* 0th argument always program name */
  3075.     temp_argv[1] = get_token(s,&s," \t\v\f"); /* option */
  3076.     temp_argv[2] = get_token(s,&s," \t\v\f"); /* value */
  3077.     temp_argv[3] = (char *)NULL;
  3078.     temp_argc = (temp_argv[2] == (char*)NULL) ? 2 : 3;
  3079.     do_args(temp_argc,temp_argv);
  3080. }
  3081.  
  3082.  
  3083. static void
  3084. do_tag_name(VOID)        /* process BibTeX citation tag */
  3085. {
  3086.     int c;
  3087.     size_t k;
  3088.  
  3089.     for (k = 0, c = get_next_non_blank();
  3090.     (c != EOF) && (c != ',') && !isspace(c);
  3091.     c = get_char(), k++)
  3092.     {
  3093.     if (k >= MAX_TOKEN)
  3094.     {
  3095.         APPEND_CHAR(current_tag,k,c);
  3096.         out_with_error(current_tag, "Citation tag too long");
  3097.         return;
  3098.     }
  3099.     current_tag[k] = (char)c;
  3100.     }
  3101.     current_tag[k] = (char)'\0';
  3102.     if (c != EOF)
  3103.     put_back(c);
  3104.     if (check_values == YES)
  3105.     check_tag();
  3106.     out_s(current_tag);
  3107.     check_length(k);
  3108. }
  3109.  
  3110.  
  3111. #if NEW_STYLE
  3112. static void
  3113. enlarge_table(PATTERN_TABLE *table)
  3114. #else /* K&R style */
  3115. static void
  3116. enlarge_table(table)
  3117. PATTERN_TABLE *table;
  3118. #endif /* NEW_STYLE */
  3119. {
  3120.     if (table->maximum_size == 0)
  3121.     table->patterns = (MATCH_PATTERN*)malloc(sizeof(MATCH_PATTERN) *
  3122.         TABLE_CHUNKS);
  3123.     else
  3124.     table->patterns = (MATCH_PATTERN*)realloc((char*)table->patterns,
  3125.         sizeof(MATCH_PATTERN) * (table->maximum_size + TABLE_CHUNKS));
  3126.                     /* NB: Sun C++ requires (char*) cast */
  3127.     if (table->patterns == (MATCH_PATTERN*)NULL)
  3128.     fatal("Out of memory for pattern table space");
  3129.     table->maximum_size += TABLE_CHUNKS;
  3130. }
  3131.  
  3132.  
  3133. #if NEW_STYLE
  3134. static void            /* issue an error message */
  3135. error(const char *msg)        /* default provided if this is NULL */
  3136. #else /* K&R style */
  3137. static void
  3138. error(msg)            /* issue an error message */
  3139. const char *msg;        /* default provided if this is NULL */
  3140. #endif /* NEW_STYLE */
  3141. {
  3142.     char *p;
  3143.  
  3144.     error_count++;
  3145.     out_flush();        /* flush all buffered output */
  3146.     at_level = 0;        /* suppress further messages */
  3147.                 /* until we have resynchronized */
  3148.  
  3149.     p = format(msg);
  3150.     (void)fprintf(stdlog,"%s \"%s\", line %ld: %s.\n",
  3151.     ERROR_PREFIX,
  3152.     the_file.input.filename, the_value.input.line_number, p);
  3153.                 /* UNIX-style error message for */
  3154.                 /* GNU Emacs M-x compile to parse */
  3155.     out_status(stdlog, ERROR_PREFIX);
  3156.     (void)fflush(stdlog);
  3157.  
  3158.     out_error(stdout, "\n"); /* make sure we start a newline */
  3159.     out_error(stdout, ERROR_PREFIX);
  3160.     out_error(stdout, " ");
  3161.     out_error(stdout, p);
  3162.     out_error(stdout, ".\n");
  3163.     out_status(stdout, ERROR_PREFIX);
  3164.     out_flush();        /* flush all buffered output */
  3165. }
  3166.  
  3167.  
  3168. #if NEW_STYLE
  3169. static void            /* issue an error message and die */
  3170. fatal(const char *msg)
  3171. #else /* K&R style */
  3172. static void
  3173. fatal(msg)            /* issue an error message and die */
  3174. const char *msg;
  3175. #endif /* NEW_STYLE */
  3176. {
  3177.     (void)fprintf(stdlog,"%s %s\n", ERROR_PREFIX, msg);
  3178.     exit(EXIT_FAILURE);
  3179. }
  3180.  
  3181.  
  3182. #if NEW_STYLE
  3183. static char*            /* normalize author names and return */
  3184. fix_author(char *author)    /* new string from static space */
  3185. #else /* K&R style */
  3186. static char*
  3187. fix_author(author)        /* normalize author names and return */
  3188. char *author;            /* new string from static space */
  3189. #endif /* NEW_STYLE */
  3190. {
  3191.     size_t a;            /* index into author[] */
  3192.     int b_level;        /* brace level */
  3193.     char *p;            /* pointer into author[] */
  3194.     static char s[MAX_TOKEN_SIZE]; /* returned to caller */
  3195.  
  3196.     /* Convert "Smith, J.K." to "J. K. Smith" provided "," and "." are */
  3197.     /* at brace level 0 */
  3198.  
  3199.     if (fix_names == NO)
  3200.     return (author);
  3201.  
  3202.     /* Leave untouched entries like: */
  3203.     /* author = "P. D. Q. Bach (113 MozartStrasse, Vienna, Austria)" */
  3204.     if (strchr(author,'(') != (char*)NULL)
  3205.     return (author);
  3206.  
  3207.     b_level = 0;
  3208.  
  3209.     for (a = 0; author[a]; ++a) /* convert "Smith, John" to "John Smith" */
  3210.     {                /* leaving period job to fix_periods() */
  3211.     switch (author[a])
  3212.     {
  3213.     case '{':
  3214.         b_level++;
  3215.         break;
  3216.  
  3217.     case '}':
  3218.         b_level--;
  3219.         break;
  3220.  
  3221.     case ',':
  3222.         if (b_level == 0)
  3223.         {
  3224.         for (p = &author[a+1]; isspace(*p); ++p)
  3225.             /* NO-OP */;
  3226.         (void)strcpy(s,p);    /* s <- "J.K." */
  3227.         (void)strcat(s," ");    /* s <- "J.K. " */
  3228.         (void)strncat(s,author,a); /* s <- "J.K. Smith" */
  3229.         return (strcpy(author,s));
  3230.         }
  3231.     }
  3232.     }
  3233.     return (author);
  3234. }
  3235.  
  3236.  
  3237. static void
  3238. fix_month(VOID)            /* convert full month names to macros*/
  3239. {                /* for better style-file customization */
  3240.     int k;
  3241.     size_t n = strlen(current_value);
  3242.  
  3243.     for (k = 0; k < 12; ++k)
  3244.     {
  3245.     if (strnicmp(current_value,month_pair[k].old_name,n) == 0)
  3246.     {            /* change "January" to jan etc. */
  3247.         (void)strcpy(current_value,month_pair[k].new_name);
  3248.         break;
  3249.     }
  3250.     else if ((n == 5) &&
  3251.          (current_value[0] == '"') &&
  3252.          (current_value[4] == '"') &&
  3253.          (strnicmp(¤t_value[1],month_pair[k].new_name,3) == 0))
  3254.     {            /* change "jan" to jan etc. */
  3255.         (void)strcpy(current_value,month_pair[k].new_name);
  3256.         break;
  3257.     }
  3258.     }
  3259. }
  3260.  
  3261.  
  3262. static void
  3263. fix_namelist(VOID)        /* normalize list of personal names */
  3264. {                /* leaving it in global current_value[] */
  3265.     size_t m;            /* index of start of author in current_value[]*/
  3266.     size_t n;            /* length of current_value[], less 1 */
  3267.     char namelist[MAX_TOKEN_SIZE];    /* working copy of current_value[] */
  3268.     size_t v;            /* loop index into current_value[] */
  3269.  
  3270.     /* Convert "Smith, J.K. and Brown, P.M." to */
  3271.     /* "J. K. Smith and P. M. Brown" */
  3272.     /* We loop over names separated by " and ", and hand each off */
  3273.     /* to fix_author() */
  3274.  
  3275.     n = strlen(current_value) - 1;    /* namelist = "\"...\"" */
  3276.  
  3277.     if ((current_value[0] != '"') ||
  3278.     (current_value[n] != '"')) /* sanity check */
  3279.     return;            /* not quoted string, may be macro */
  3280.  
  3281.     (void)strcpy(namelist,"\"");/* supply initial quotation mark */
  3282.     current_value[n] = (char)'\0';    /* clobber final quotation mark */
  3283.     for (v = 1, m = 1; v < n; ++v) /* start past initial quotation mark */
  3284.     {
  3285.     if (strncmp(" and ",¤t_value[v],5) == 0)
  3286.     {
  3287.         current_value[v] = (char)'\0';
  3288.         (void)strcat(namelist,fix_periods(fix_author(¤t_value[m])));
  3289.         (void)strcat(namelist," and ");
  3290.         current_value[v] = (char)' ';
  3291.         v += 4;
  3292.         m = v + 1;
  3293.     }
  3294.     else if ((Scribe == YES) && (current_value[v] == ';'))
  3295.     {                /* expand semicolons to " and " */
  3296.         current_value[v] = (char)'\0';
  3297.         (void)strcat(namelist,fix_periods(fix_author(¤t_value[m])));
  3298.         (void)strcat(namelist," and ");
  3299.         current_value[v] = (char)' ';
  3300.         m = v + 1;
  3301.     }
  3302.     }
  3303.     (void)strcat(namelist,fix_periods(fix_author(¤t_value[m])));
  3304.                     /* handle last author */
  3305.     (void)strcat(namelist,"\"");    /* supply final quotation mark */
  3306.     (void)strcpy(current_value,namelist);
  3307. }
  3308.  
  3309.  
  3310. static void
  3311. fix_pages(VOID)
  3312. {
  3313.     size_t k;            /* index into current_value[] */
  3314.     size_t m;            /* index into new_value[] */
  3315.     char new_value[MAX_TOKEN_SIZE]; /* working copy of new_value[] */
  3316.  
  3317.     for (m = 0, k = 0; current_value[k]; ++k)
  3318.     {                /* squeeze out spaces around hyphens */
  3319.                 /* and convert hyphen runs to en-dashes */
  3320.     if (current_value[k] == '-')
  3321.     {            /* convert hyphens to en-dash */
  3322.         for ( ; (m > 0) && isspace(new_value[m-1]) ; )
  3323.         --m;        /* discard preceding spaces */
  3324.         for ( ; current_value[k+1] == '-'; )
  3325.         ++k;
  3326.         for ( ; isspace(current_value[k+1]); )
  3327.         ++k;        /* discard following spaces */
  3328.         new_value[m++] = (char)'-'; /* save an en-dash */
  3329.         new_value[m++] = (char)'-';
  3330.     }
  3331.     else
  3332.         new_value[m++] = current_value[k];
  3333.     }
  3334.     new_value[m] = (char)'\0';
  3335.     (void)strcpy(current_value,new_value);
  3336. }
  3337.  
  3338.  
  3339. #if NEW_STYLE
  3340. static char*
  3341. fix_periods(char *author)
  3342. #else /* K&R style */
  3343. static char*
  3344. fix_periods(author)
  3345. char *author;
  3346. #endif /* NEW_STYLE */
  3347. {
  3348.     int b_level;            /* brace level */
  3349.     size_t a;                /* index in author[] */
  3350.     size_t n;                /* index in name[] */
  3351.     char *name = shared_string;        /* memory-saving device */
  3352.  
  3353.     if (fix_initials == NO)
  3354.       return author;
  3355.  
  3356.     /* Convert "J.K. Smith" to "J. K. Smith" if "." at brace level 0 */
  3357.  
  3358.     for (b_level = 0, a = 0, n = 0; /* NO-OP (exit below) */ ; ++a, ++n)
  3359.     {
  3360.     name[n] = author[a];        /* copy character */
  3361.     if (author[a] == '\0')
  3362.         break;            /* here's the loop exit */
  3363.     switch (author[a])
  3364.     {
  3365.     case '{':
  3366.         b_level++;
  3367.         break;
  3368.  
  3369.     case '}':
  3370.         b_level--;
  3371.         break;
  3372.  
  3373.     case '.':
  3374.         if (b_level == 0)
  3375.         {
  3376.         if ((a > 0) && isupper(author[a-1]) && isupper(author[a+1]))
  3377.             name[++n] = (char)' '; /* supply space between initials */
  3378.         }
  3379.         break;
  3380.     }
  3381.     }
  3382.     return (name);
  3383. }
  3384.  
  3385.  
  3386. static void
  3387. fix_title(VOID)                /* protect upper-case acronyms */
  3388. {
  3389.     YESorNO brace_letter;
  3390.     int b_level;            /* brace level */
  3391.     size_t k;                /* index into s[] */
  3392.     char *s = shared_string;        /* memory-saving device */
  3393.     size_t t;                /* index into title[] */
  3394.  
  3395.     if (current_value[0] != '\"')
  3396.     return;                    /* leave macros alone */
  3397.  
  3398.     for (k = 0, b_level = 0, t = 0; current_value[t]; )
  3399.     {
  3400.     switch (current_value[t])
  3401.     {
  3402.     case '{':
  3403.         b_level++;
  3404.         s[k++] = current_value[t++];
  3405.         break;
  3406.  
  3407.     case '}':
  3408.         b_level--;
  3409.         s[k++] = current_value[t++];
  3410.         break;
  3411.  
  3412.     default:
  3413.         if (b_level > 0)
  3414.         brace_letter = NO;    /* already braced, so no changes */
  3415.         else if (isupper(current_value[t]))    /* maybe brace <upper-case>+ */
  3416.         {                /* or <upper-case><digits> */
  3417.         if (isupper(current_value[t+1]) || isdigit(current_value[t+1]))
  3418.             brace_letter = YES;    /* XY -> {XY}, X11 -> {X11} */
  3419.         else if (!isalpha(current_value[t+1]))
  3420.         {
  3421.             if ((t == 1) && (current_value[t] == 'A'))
  3422.             brace_letter = NO; /* "A gnat" -> "A gnat" */
  3423.             else
  3424.             brace_letter = YES; /* "The C book" -> "The {C} Book" */
  3425.         }
  3426.         else
  3427.             brace_letter = NO;    /* everything else unchanged */
  3428.         }
  3429.         else
  3430.         brace_letter = NO;
  3431.         if (brace_letter)
  3432.         {            /* Convert XWS to {XWS} and X11 to {X11} */
  3433.         s[k++] = (char)'{';
  3434.         while (isupper(current_value[t]) || isdigit(current_value[t]))
  3435.             s[k++] = current_value[t++];
  3436.         s[k++] = (char)'}';
  3437.         }
  3438.         else
  3439.         s[k++] = current_value[t++];
  3440.         break;
  3441.     }
  3442.     }
  3443.     s[k] = (char)'\0';
  3444.     check_length(k);
  3445.     (void)strcpy(current_value,s);
  3446. }
  3447.  
  3448.  
  3449. static void
  3450. flush_inter_entry_space(VOID) /* standardize to 1 blank line between entries */
  3451. {
  3452.     int c;
  3453.  
  3454.     put_back((c = get_next_non_blank()));
  3455.     if (c != EOF)
  3456.     out_c('\n'), out_c('\n');
  3457.     else
  3458.     out_c('\n');
  3459. }
  3460.  
  3461.  
  3462. #if NEW_STYLE
  3463. static char*
  3464. format(const char *msg)
  3465. #else /* K&R style */
  3466. static char*
  3467. format(msg)
  3468. const char *msg;
  3469. #endif /* NEW_STYLE */
  3470. {   /* expand %k, %t, %v, and %% items in msg[], return pointer to new copy */
  3471.     size_t k;
  3472.     size_t len;
  3473.     size_t n;
  3474.     char *newmsg = shared_string;        /* memory-saving device */
  3475.  
  3476.     /* Shorthand for writable copy of msg[] with guaranteed NUL termination */
  3477. #define ORIGINAL_MESSAGE (strncpy(newmsg,msg,MAX_TOKEN_SIZE), \
  3478.     newmsg[MAX_TOKEN_SIZE-1] = (char)'\0', newmsg)
  3479.  
  3480.     for (k = 0, n = 0; msg[k]; ++k)
  3481.     {
  3482.     switch (msg[k])
  3483.     {
  3484.     case '%':            /* expect valid format item */
  3485.         switch (msg[++k])
  3486.         {
  3487.         case 'e':            /* %e -> current_entry_name */
  3488.         len = strlen(current_entry_name);
  3489.         if ((n + len) >= MAX_TOKEN_SIZE)
  3490.             return (ORIGINAL_MESSAGE); /* no space left*/
  3491.         (void)strcpy(&newmsg[n],current_entry_name);
  3492.         n += len;
  3493.         break;
  3494.  
  3495.         case 'k':            /* %k -> current_key */
  3496.         len = strlen(current_key);
  3497.         if ((n + len) >= MAX_TOKEN_SIZE)
  3498.             return (ORIGINAL_MESSAGE); /* no space left*/
  3499.         (void)strcpy(&newmsg[n],current_key);
  3500.         n += len;
  3501.         break;
  3502.  
  3503.         case 't':            /* %t -> current_tag */
  3504.         len = strlen(current_tag);
  3505.         if ((n + len) >= MAX_TOKEN_SIZE)
  3506.             return (ORIGINAL_MESSAGE); /* no space left*/
  3507.         (void)strcpy(&newmsg[n],current_tag);
  3508.         n += len;
  3509.         break;
  3510.  
  3511.         case 'v':            /* %v -> current_value */
  3512.         len = strlen(current_value);
  3513.         if ((n + len) >= MAX_TOKEN_SIZE)
  3514.             return (ORIGINAL_MESSAGE); /* no space left*/
  3515.         (void)strcpy(&newmsg[n],current_value);
  3516.         n += len;
  3517.         break;
  3518.  
  3519.         case '%':            /* %% -> % */
  3520.         newmsg[n++] = (char)'%';
  3521.         break;
  3522.  
  3523.         default:
  3524.         return (ORIGINAL_MESSAGE); /* no space left*/
  3525.         }
  3526.         break;
  3527.  
  3528.     default:
  3529.         if (n >= MAX_TOKEN_SIZE)
  3530.         return (ORIGINAL_MESSAGE); /* no space left*/
  3531.         newmsg[n++] = msg[k];
  3532.         break;
  3533.     }
  3534.     }
  3535.     newmsg[n] = (char)'\0';            /* terminate string */
  3536.     return (newmsg);
  3537. }
  3538.  
  3539.  
  3540. static char *
  3541. get_braced_string(VOID)
  3542. {
  3543.     int b_level = 0;        /* brace level */
  3544.     int c;            /* current input character */
  3545.     size_t k;            /* index into s[] */
  3546.     size_t n;            /* index into t[] */
  3547.     char *s = shared_string;    /* memory-saving device */
  3548.     char t[MAX_TOKEN_SIZE];    /* working area for braced string */
  3549.  
  3550.     for (c = get_char(), k = 0; c != EOF; )
  3551.     {
  3552.     if ((parbreaks == NO) && (is_parbreak == YES))
  3553.     {
  3554.         APPEND_CHAR(s,k,c);
  3555.         out_with_parbreak_error(s);
  3556.         return (EMPTY_STRING(s));
  3557.     }
  3558.     else if (k >= MAX_TOKEN)
  3559.     {
  3560.         APPEND_CHAR(s,k,c);
  3561.         out_with_error(s, "BibTeX string too long for key ``%k''");
  3562.         return (EMPTY_STRING(s));
  3563.     }
  3564.     else
  3565.     {
  3566.         if (isspace(c))
  3567.         c = ' ';        /* change whitespace to real space */
  3568.         else if (c == '{')
  3569.         b_level++;
  3570.         else if (c == '}')
  3571.         b_level--;
  3572.         s[k++] = (char)c;
  3573.         if (b_level == 0)
  3574.         break;            /* here's the loop exit */
  3575.         c = isspace(c) ? get_next_non_blank() : get_char();
  3576.     }
  3577.     }
  3578.     s[k] = (char)'\0';
  3579.  
  3580.     /* Now convert braced string to quoted string */
  3581.  
  3582.     for (b_level = 0, k = 0, n = 0; s[k]; ++k)
  3583.     {
  3584.     if (s[k] == '{')
  3585.         b_level++;
  3586.     else if (s[k] == '}')
  3587.         b_level--;
  3588.     if ((s[k] == '"') && (b_level == 1))    /* k > 0 if this is true */
  3589.     {                    /* so we can omit that check */
  3590.         if (s[k-1] == '\\')            /* change \"xy to {\"x}y */
  3591.         n--,
  3592.             t[n++] = (char)'{', t[n++] = (char)'\\', t[n++] = (char)'"',
  3593.         t[n++] = s[++k], t[n++] = (char)'}';
  3594.         else                /* change x" to x{"} */
  3595.         t[n++] = (char)'{', t[n++] = (char)'"', t[n++] = (char)'}';
  3596.     }
  3597.     else
  3598.         t[n++] = s[k];
  3599.     }
  3600.     t[0] = (char)'"';                /* change initial and final */
  3601.     APPEND_CHAR(t,n-1,'"');            /* braces to quotes */
  3602.     check_length(n);
  3603.     return (strcpy(s,t));
  3604. }
  3605.  
  3606.  
  3607. static int
  3608. get_char(VOID)            /* all input is read through this function */
  3609. {
  3610.     int c;
  3611.  
  3612.     c = getc(fpin); /* NB: this is the ONLY place where input file is read! */
  3613.  
  3614.     the_file.input.byte_position++;
  3615.  
  3616.     /* Adjust global status and position values */
  3617.  
  3618.     if (c == EOF)
  3619.     eofile = YES;
  3620.     else if (c == '\n')
  3621.     {
  3622.     the_file.input.line_number++;
  3623.     the_file.input.last_column_position = the_file.input.column_position;
  3624.     the_file.input.column_position = 0L;
  3625.     non_white_chars = 0;
  3626.     }
  3627.     else if (!isspace(c))
  3628.     {
  3629.     the_file.input.last_column_position = the_file.input.column_position;
  3630.     the_file.input.column_position++;
  3631.     non_white_chars++;
  3632.     }
  3633.     else if (c == '\t')
  3634.     {
  3635.     the_file.input.last_column_position = the_file.input.column_position;
  3636.     the_file.input.column_position =
  3637.         (the_file.input.column_position + 8L) & ~07L;
  3638.     }
  3639.     else
  3640.     {
  3641.     the_file.input.last_column_position = the_file.input.column_position;
  3642.     the_file.input.column_position++;
  3643.     }
  3644.  
  3645.     if (c == '{')
  3646.     brace_level++;
  3647.     else if (c == '}')
  3648.     brace_level--;
  3649.  
  3650. #if defined(DEBUG)
  3651.     if (fpdebug)
  3652.     (void)fprintf(fpdebug,"[%c] %5ld %4ld %2ld\n",
  3653.         c,
  3654.         the_file.input.byte_position,
  3655.         the_file.input.line_number,
  3656.         the_file.input.column_position);
  3657. #endif /* defined(DEBUG) */
  3658.  
  3659.     return (c);
  3660. }
  3661.  
  3662.  
  3663. static char *
  3664. get_digit_string(VOID)
  3665. {
  3666.     int c;            /* current input character */
  3667.     size_t k;            /* index into s[] */
  3668.     char *s = shared_string;    /* memory-saving device */
  3669.  
  3670.     k = 0;
  3671.     s[k++] = (char)'"';            /* convert to quoted string */
  3672.     for (c = get_char(); (c != EOF) && isdigit(c); )
  3673.     {
  3674.     if (k >= MAX_TOKEN)
  3675.     {
  3676.         APPEND_CHAR(s,k,c);
  3677.         out_with_error(s, "BibTeX string too long for key ``%k''");
  3678.         return (EMPTY_STRING(s));
  3679.     }
  3680.     else
  3681.     {
  3682.         s[k++] = (char)c;
  3683.         c = get_char();
  3684.     }
  3685.     }
  3686.     put_back(c);            /* we read past end of digit string */
  3687.     s[k++] = (char)'"';            /* supply terminating quote */
  3688.     s[k] = (char)'\0';
  3689.     check_length(k);
  3690.     return (s);
  3691. }
  3692.  
  3693.  
  3694. static char *
  3695. get_identifier_string(VOID)
  3696. {
  3697.     int c;            /* current input character */
  3698.     size_t k;            /* index into s[] */
  3699.     char *s = shared_string;    /* memory-saving device */
  3700.  
  3701.     for (c = get_char(), k = 0; (c != EOF) && isidchar(c); )
  3702.     {
  3703.     if (k >= MAX_TOKEN)
  3704.     {
  3705.         APPEND_CHAR(s,k,c);
  3706.         out_with_error(s, "BibTeX string too long for key ``%k''");
  3707.         return (EMPTY_STRING(s));
  3708.     }
  3709.     else
  3710.     {
  3711.         s[k++] = (char)c;
  3712.         c = get_char();
  3713.     }
  3714.     }
  3715.     put_back(c);        /* we read past end of identifier string */
  3716.     s[k] = (char)'\0';
  3717.     check_length(k);
  3718.     return (s);
  3719. }
  3720.  
  3721.  
  3722. #if NEW_STYLE
  3723. static char*
  3724. get_line(FILE *fp)
  3725. #else /* K&R style */
  3726. static char*
  3727. get_line(fp)
  3728. FILE    *fp;
  3729. #endif /* NEW_STYLE */
  3730. {   /* return a complete line to the caller, discarding backslash-newlines */
  3731.     /* on consecutive lines, and discarding the final newline.  At EOF, */
  3732.     /* return (char*)NULL instead. */
  3733.     static char line[MAX_LINE];
  3734.     static char *p;
  3735.     static char *more;
  3736.  
  3737.     more = &line[0];
  3738.     line[0] = (char)'\0';        /* must set in case we hit EOF */
  3739.     while (fgets(more,(int)(&line[MAX_LINE] - more),fp) != (char *)NULL)
  3740.     {
  3741.     p = strchr(more,'\n');
  3742.     if (p != (char*)NULL)        /* did we get a complete line? */
  3743.     {                /* yes */
  3744.         *p = '\0';            /* zap final newline */
  3745.         if (*(p-1) == '\\')        /* then have backslash-newline */
  3746.         more = p - 1;        /* so get another line */
  3747.         else            /* otherwise have normal newline */
  3748.         break;            /* so return the current line */
  3749.     }
  3750.     else                /* no, return partial line */
  3751.         break;
  3752.     }
  3753.     return ((line[0] == '\0' && feof(fp)) ? (char*)NULL : &line[0]);
  3754. }
  3755.  
  3756.  
  3757. static int
  3758. get_next_non_blank(VOID)
  3759. {
  3760.     int c;
  3761.     int ff = 0;
  3762.     int nl = 0;
  3763.  
  3764.     while (((c = get_char()) != EOF) && isspace(c))
  3765.     {
  3766.     switch (c)
  3767.     {
  3768.     case '\n':
  3769.         nl++;
  3770.         break;
  3771.     case '\f':
  3772.         ff++;
  3773.         break;
  3774.     }
  3775.     }
  3776.     is_parbreak = ((nl > 1) || (ff > 0)) ? YES : NO;
  3777.     return (c);
  3778. }
  3779.  
  3780.  
  3781. static char *
  3782. get_quoted_string(VOID)
  3783. {
  3784.     int b_level = 0;        /* brace level */
  3785.     int c;            /* current input character */
  3786.     size_t k;            /* index into s[] */
  3787.     char *s = shared_string;    /* memory-saving device */
  3788.  
  3789.     for (c = get_char(), k = 0; c != EOF; )
  3790.     {
  3791.     if ((parbreaks == NO) && (is_parbreak == YES))
  3792.     {
  3793.         APPEND_CHAR(s,k,c);
  3794.         out_with_parbreak_error(s);
  3795.         return (EMPTY_STRING(s));
  3796.     }
  3797.     else if (k >= MAX_TOKEN)
  3798.     {
  3799.         APPEND_CHAR(s,k,c);
  3800.         out_with_error(s, "BibTeX string too long for key ``%k''");
  3801.         return (EMPTY_STRING(s));
  3802.     }
  3803.     else
  3804.     {
  3805.         if (isspace(c))
  3806.             c = ' ';        /* change whitespace to real space */
  3807.         else if (c == '{')
  3808.             b_level++;
  3809.         else if (c == '}')
  3810.             b_level--;
  3811.         s[k++] = (char)c;
  3812.         if ((c == '"') && (k > 1) && (b_level == 0))
  3813.         {
  3814.         if (s[k-2] == '\\')
  3815.         {
  3816.             /* convert \"x inside string at brace-level 0 to {\"x}: */
  3817.             /* illegal, but hand-entered bibliographies have it */
  3818.             c = get_char();
  3819.             if (c != EOF)
  3820.             {
  3821.             k = k - 2;
  3822.             s[k++] = (char)'{';
  3823.             s[k++] = (char)'\\';
  3824.             s[k++] = (char)'"';
  3825.             s[k++] = (char)c;
  3826.             s[k++] = (char)'}';
  3827.             }
  3828.         }
  3829.         else
  3830.             break;        /* here's the loop exit */
  3831.  
  3832.         }
  3833.         c = isspace(c) ? get_next_non_blank() : get_char();
  3834.     }
  3835.     }
  3836.     s[k] = (char)'\0';
  3837.     check_length(k);
  3838.     return (s);
  3839. }
  3840.  
  3841.  
  3842. static char *
  3843. get_Scribe_delimited_string(VOID)
  3844. {
  3845.     int c;
  3846.     int close_delim;
  3847.     size_t k;
  3848.     int last_c = EOF;
  3849.     char *p;
  3850.     char *s = shared_string;        /* memory-saving device */
  3851.  
  3852.     c = get_next_non_blank();
  3853.     p = strchr(Scribe_open_delims,c);    /* maybe delimited string? */
  3854.  
  3855.     if (p == (char*)NULL)
  3856.     {
  3857.     APPEND_CHAR(s,0,c);
  3858.     out_with_error(s,"Expected Scribe value string for key ``%k''");
  3859.     return (EMPTY_STRING(s));
  3860.     }
  3861.  
  3862.     /* We have a delimited string */
  3863.  
  3864.     close_delim = Scribe_close_delims[(int)(p - Scribe_open_delims)];
  3865.  
  3866.     c = get_next_non_blank();        /* get first character in string */
  3867.                     /* ignoring leading space */
  3868.     for (k = 0, s[k++] = (char)'"';
  3869.      (c != EOF) &&
  3870.      !((last_c != '\\') && (c == close_delim)) &&
  3871.      (k < MAX_TOKEN);
  3872.      k++)
  3873.     {
  3874.     if ((parbreaks == NO) && (is_parbreak == YES))
  3875.     {
  3876.         APPEND_CHAR(s,k,c);
  3877.         out_with_parbreak_error(s);
  3878.         return (EMPTY_STRING(s));
  3879.     }
  3880.     if (c == '"')            /* protect quotes inside string */
  3881.     {
  3882.         if (s[k-1] == '\\')
  3883.         {                /* then TeX accent in Scribe string */
  3884.         last_c = c;
  3885.         c = get_char();
  3886.         if (c == '{')        /* change \"{ to {\" */
  3887.         {
  3888.             s[k-1] = (char)'{';
  3889.             s[k] = (char)'\\';
  3890.             s[++k] = (char)'"';
  3891.         }
  3892.         else            /* change \". to {\".} (. = any) */
  3893.         {
  3894.             s[k-1] = (char)'{';
  3895.             s[k] = (char)'\\';
  3896.             s[++k] = (char)'"';
  3897.             s[++k] = (char)c;
  3898.             s[++k] = (char)'}';
  3899.         }
  3900.         }
  3901.         else
  3902.         {
  3903.         s[k] = (char)'{';
  3904.         s[++k] = (char)'"';
  3905.         s[++k] = (char)'}';
  3906.         }
  3907.     }
  3908.     else if (isspace(c))
  3909.         s[k] = (char)' ';        /* change whitespace to real space */
  3910.     else
  3911.         s[k] = (char)c;
  3912.     last_c = c;
  3913.     c = isspace(c) ? get_next_non_blank() : get_char();
  3914.     }
  3915.  
  3916.     APPEND_CHAR(s,k,'"');        /* append close delimiter */
  3917.     if (k >= MAX_TOKEN)
  3918.     {
  3919.     out_with_error(s, "Scribe string too long for key ``%k''");
  3920.     return (EMPTY_STRING(s));
  3921.     }
  3922.     check_length(k);
  3923.     return (s);
  3924. }
  3925.  
  3926.  
  3927. static char *
  3928. get_Scribe_identifier_string(VOID)    /* read undelimited identifier */
  3929. {                    /* and return quoted equivalent */
  3930.     int c;
  3931.     size_t k;
  3932.     char *s = shared_string;        /* memory-saving device */
  3933.  
  3934.     c = get_next_non_blank();
  3935.     for (k = 0, s[k++] = (char)'"'; isidchar(c) && (k < MAX_TOKEN);
  3936.     k++, c = get_char())
  3937.     {
  3938.     s[k] = (char)c;
  3939.     }
  3940.     put_back(c);            /* put back lookahead */
  3941.     APPEND_CHAR(s,k,'"');
  3942.     if (k >= MAX_TOKEN)
  3943.     {
  3944.     out_with_error(s, "Scribe number string too long for key ``%k''");
  3945.     return (EMPTY_STRING(s));
  3946.     }
  3947.     check_length(++k);
  3948.     return (s);
  3949. }
  3950.  
  3951.  
  3952. static char *
  3953. get_Scribe_string(VOID)            /* read Scribe string */
  3954. {
  3955.     int c;
  3956.  
  3957.     c = get_next_non_blank();        /* peek ahead one character */
  3958.     put_back(c);
  3959.  
  3960.     return (isidchar(c) ?
  3961.         get_Scribe_identifier_string() :
  3962.         get_Scribe_delimited_string());
  3963. }
  3964.  
  3965.  
  3966. static char *
  3967. get_simple_string(VOID)        /* read simple BibTeX string */
  3968. {
  3969.     int c;            /* current input character */
  3970.     char *s = shared_string;    /* memory-saving device */
  3971.  
  3972.     c = get_next_non_blank();    /* peek ahead to next non-blank */
  3973.  
  3974.     if ((parbreaks == NO) && (is_parbreak == YES))
  3975.     {
  3976.     APPEND_CHAR(s,0,c);
  3977.     out_with_parbreak_error(s);
  3978.     return (EMPTY_STRING(s));
  3979.     }
  3980.  
  3981.     put_back(c);        /* put back lookahead */
  3982.  
  3983.     if (c == '{')
  3984.         return (get_braced_string());
  3985.     else if (isdigit(c))
  3986.         return (get_digit_string());
  3987.     else if (c == '"')
  3988.         return (get_quoted_string());
  3989.     else if (isalpha(c))
  3990.     return (get_identifier_string());
  3991.     else
  3992.     {
  3993.     out_with_error("", "Expected BibTeX value string for key ``%k''");
  3994.     return (EMPTY_STRING(s));
  3995.     }
  3996. }
  3997.  
  3998.  
  3999. #if NEW_STYLE
  4000. static char*
  4001. get_token(char *s, char **nextp, const char *terminators)
  4002. #else /* K&R style */
  4003. static char*
  4004. get_token(s,nextp,terminators)
  4005. char *s;
  4006. char **nextp;
  4007. const char *terminators;
  4008. #endif /* NEW_STYLE */
  4009. {
  4010.     char *t = s;
  4011.     char *token;
  4012.  
  4013.     /*******************************************************************
  4014.       Ignoring leading space, find the next token in s[], stopping at
  4015.       end-of-string, or one of the characters in terminators[],
  4016.       whichever comes first.  Replace the terminating character in s[]
  4017.       by a NUL.  Set *nextp to point to the next character in s[], or to
  4018.       (char*)NULL if end-of-string was reached.  Return (char*)NULL if
  4019.       no token was found, or else a pointer to its start in s[].  The
  4020.       job is terminated with an error message if a syntax error is
  4021.       detected.
  4022.  
  4023.       Quoted strings are correctly recognized as valid tokens, and
  4024.       returned with their surrounding quotes removed, and embedded
  4025.       escape sequences expanded.  The comment character is recognized
  4026.       outside quoted strings, but not inside.
  4027.       *******************************************************************/
  4028.  
  4029.     SKIP_SPACE(t);
  4030.  
  4031.     if ((t == (char*)NULL) || (*t == '\0') || (*t == COMMENT_PREFIX))
  4032.     {                    /* initial sanity check */
  4033.     t = (char*)NULL;        /* save for *nextp later */
  4034.     token = (char*)NULL;
  4035.     }
  4036.     else if (*t == '"')            /* then collect quoted string */
  4037.     {
  4038.     token = ++t;            /* drop leading quote */
  4039.     for ( ; *t && (*t != '"'); ++t)
  4040.     {                /* find ending quote */
  4041.         /* step over escape sequences; it doesn't matter if we have */
  4042.         /* \123, since we are only looking for the ending quote */
  4043.         if (*t == '\\')
  4044.         ++t;
  4045.     }
  4046.     if (*t == '"')            /* then found valid string */
  4047.     {
  4048.         *t++ = '\0';        /* terminate token */
  4049.         do_escapes(token);        /* and expand escape sequences */
  4050.     }
  4051.     else
  4052.     {
  4053.         (void)fprintf(stdlog,
  4054.         "%s Bad line [%s] in initialization file [%s]\n",
  4055.         ERROR_PREFIX, s, initialization_file_name);
  4056.         exit(EXIT_FAILURE);
  4057.     }
  4058.     }
  4059.     else                /* else collect unquoted string */
  4060.     {
  4061.     for (token = t; *t && (*t != COMMENT_PREFIX) &&
  4062.         (strchr(terminators,*t) == (char*)NULL); ++t)
  4063.         NOOP;            /* scan over token */
  4064.  
  4065.     if ((*t == '\0') || (*t == COMMENT_PREFIX)) /* then hit end of s[] */
  4066.         t = (char*)NULL;        /* save for *nextp later */
  4067.     else                /* else still inside s[] */
  4068.         *t++ = '\0';        /* terminate token */
  4069.     }
  4070.     *nextp = t;                /* set continuation position */
  4071.     return (token);
  4072. }
  4073.  
  4074.  
  4075. #if NEW_STYLE
  4076. static int
  4077. isidchar(int c)
  4078. #else /* K&R style */
  4079. static int
  4080. isidchar(c)
  4081. int c;
  4082. #endif /* NEW_STYLE */
  4083. {
  4084.     /* See LaTeX User's Guide and Reference Manual, Section B.1.3, for the
  4085.        rules of what characters can be used in a BibTeX word value.  Section
  4086.        30 of BibTeX initializes id_class[] to match this, but curiously,
  4087.        allows ASCII DELete (0x3f), as an identifier character.  This
  4088.        irregularity has been reported to Oren Patashnik on
  4089.        [06-Oct-1990].  We disallow it here.
  4090.  
  4091.        The Scribe syntax is simpler: letters, digits, ., #, &, and %. */
  4092.  
  4093.     return ((Scribe == YES) ?
  4094.         (isalnum(c) || (c == '.') || (c == '#') ||
  4095.                (c == '&') || (c == '%') ) :
  4096.         (isgraph(c) && (strchr("\"#%'(),={}",c) == (char*)NULL)) );
  4097. }
  4098.  
  4099.  
  4100. #if (OS_PCDOS && (SCREEN_LINES > 0))
  4101.  
  4102. #include <conio.h>        /* needed for getch() declaration */
  4103.  
  4104. static int
  4105. get_screen_lines(VOID)
  4106. {
  4107.     return (SCREEN_LINES);
  4108. }
  4109.  
  4110.  
  4111. static void
  4112. kbclose(VOID)
  4113. {
  4114. }
  4115.  
  4116.  
  4117. static KEYCODE
  4118. kbcode(VOID)
  4119. {
  4120.     int c;
  4121.  
  4122.     c = kbget();            /* get from keyboard without echo */
  4123.     if ((c == 0) || (c == 0xe0))    /* then have IBM PC function key */
  4124.     {
  4125.     c = kbget();            /* function key code */
  4126.     switch (c)            /* convert key code to character */
  4127.     {
  4128.     case 71:            /* HOME */
  4129.         return (KEY_HOME);
  4130.  
  4131.     case 72:            /* UP arrow */
  4132.         return (KEY_UP);
  4133.  
  4134.     case 73:            /* PGUP */
  4135.         return (KEY_PGUP);
  4136.  
  4137.     case 79:            /* END */
  4138.         return (KEY_END);
  4139.  
  4140.     case 80:            /* DOWN arrow */
  4141.         return (KEY_DOWN);
  4142.  
  4143.     case 81:            /* PGDN */
  4144.         return (KEY_PGDN);
  4145.  
  4146.     default:
  4147.         return (KEY_UNKNOWN);
  4148.     }
  4149.     }
  4150.     else if (c == EOF)
  4151.     return (KEY_EOF);
  4152.     else
  4153.     return (keymap[(unsigned)c]);
  4154. }
  4155.  
  4156.  
  4157. static int
  4158. kbget(VOID)
  4159. {
  4160.     return (getch());
  4161. }
  4162.  
  4163.  
  4164. static void
  4165. kbopen(VOID)
  4166. {
  4167.     kbinitmap();
  4168. }
  4169. #endif /* (OS_PCDOS && (SCREEN_LINES > 0)) */
  4170.  
  4171.  
  4172. #if (OS_UNIX && (SCREEN_LINES > 0))
  4173.  
  4174. /* One of HAVE_SGTTY_H, HAVE_TERMIO_H, or HAVE_TERMIOS_H can be
  4175. defined at compile time.  If more than one is set, we use the first
  4176. one set in that order.  Usually, the UNIX_BSD or _POSIX_SOURCE values
  4177. are sufficient to distinguish between the three cases, and no
  4178. compile-time setting is necessary.  DECstation ULTRIX has all three,
  4179. making it impossible to use symbols defined in sgtty.h, termio.h, and
  4180. termios.h to select code fragments below. */
  4181.  
  4182. #if !(defined(HAVE_SGTTY_H)||defined(HAVE_TERMIO_H)||defined(HAVE_TERMIOS_H))
  4183. #if    UNIX_BSD
  4184. #define HAVE_SGTTY_H    1
  4185. #else /* NOT UNIX_BSD */
  4186. #if   defined(_POSIX_SOURCE)
  4187. #define HAVE_TERMIOS_H    1
  4188. #else /* NOT BSD or POSIX, perhaps its AT&T System V */
  4189. #define HAVE_TERMIO_H    1
  4190. #endif /* defined(_POSIX_SOURCE) */
  4191. #endif /* UNIX_BSD */
  4192. #endif /* !(defined(HAVE_SGTTY_H) || defined(HAVE_TERMIO_H) ||
  4193.     defined(HAVE_TERMIOS_H)) */
  4194.  
  4195. static void    reset_terminal ARGS((void));
  4196. static void    set_terminal ARGS((void));
  4197.  
  4198. static FILE    *fptty = (FILE*)NULL;    /* for kbxxx() functions */
  4199. static YESorNO    tty_init = NO;        /* set to YES if tty_save set */
  4200.  
  4201.  
  4202. static void
  4203. kbclose(VOID)
  4204. {
  4205.     reset_terminal();
  4206.     if (fptty != (FILE*)NULL)
  4207.     (void)fclose(fptty);
  4208. }
  4209.  
  4210.  
  4211. static KEYCODE
  4212. kbcode(VOID)
  4213. {
  4214.     int c = kbget();
  4215.  
  4216.     if (c == EOF)
  4217.         return (KEY_EOF);
  4218.     else
  4219.     return (keymap[(unsigned)c]);
  4220. }
  4221.  
  4222.  
  4223. static int
  4224. kbget(VOID)
  4225. {
  4226.     if (fptty != (FILE*)NULL)
  4227.     {
  4228.     (void)fflush(fptty);
  4229.     return (getc(fptty));
  4230.     }
  4231.     else
  4232.     return (EOF);
  4233. }
  4234.  
  4235.  
  4236. static void
  4237. kbopen(VOID)
  4238. {
  4239.     kbinitmap();
  4240.     if ((fptty = tfopen("/dev/tty","r")) != (FILE*)NULL)
  4241.     {
  4242.     set_terminal();
  4243.     screen_lines = get_screen_lines();
  4244.     }
  4245. }
  4246.  
  4247.  
  4248. #if defined(HAVE_SGTTY_H)
  4249. #undef HAVE_TERMIO_H
  4250. #undef HAVE_TERMIOS_H
  4251. #include <sgtty.h>
  4252. #include <sys/ioctl.h>
  4253. static struct sgttyb tty_save;            /* Berkeley style interface */
  4254.  
  4255.  
  4256. static void
  4257. reset_terminal(VOID)            /* restored saved terminal modes */
  4258. {
  4259.     if (tty_init == YES)
  4260.     (void)ioctl((int)(fileno(fptty)),(int)TIOCSETP,(char*)&tty_save);
  4261. }
  4262.  
  4263.  
  4264. static void
  4265. set_terminal(VOID)        /* set terminal for cbreak input mode */
  4266. {
  4267.     struct sgttyb tty;
  4268.  
  4269.     /* Try to put file into cbreak mode for character-at-a-time input */
  4270.     if (ioctl((int)(fileno(fptty)),(int)TIOCGETP,(char*)&tty) != -1)
  4271.     {
  4272.     tty_save = tty;
  4273.     tty_init = YES;
  4274.     tty.sg_flags &= ~(ECHO | LCASE);
  4275.     tty.sg_flags |= CBREAK;
  4276.     (void)ioctl((int)(fileno(fptty)),(int)TIOCSETP,(char*)&tty);
  4277.     }
  4278. }
  4279. #endif /* defined(HAVE_SGTTY_H) */
  4280.  
  4281. #if defined(HAVE_TERMIO_H)
  4282. #undef HAVE_SGTTY_H
  4283. #undef HAVE_TERMIOS_H
  4284. #include <termio.h>
  4285. static struct termio tty_save;            /* SVID2 and XPG2 interface */
  4286.  
  4287. static void
  4288. reset_terminal(VOID)                /* restore saved modes */
  4289. {
  4290.     if (tty_init == YES)
  4291.     (void)ioctl((int)(fileno(fptty)),(int)TCSETAF,(char*)&tty_save);
  4292. }
  4293.  
  4294.  
  4295. static void
  4296. set_terminal(VOID)                /* set to cbreak input mode */
  4297. {
  4298.     struct termio tty;            /* SVID2, XPG2 interface */
  4299.  
  4300.     if (ioctl((int)(fileno(fptty)),(int)TCGETA,(char*)&tty) != -1)
  4301.     {
  4302.     tty_save = tty;
  4303.     tty_init = YES;
  4304.     tty.c_iflag &= ~(INLCR | ICRNL | ISTRIP | IXON | BRKINT);
  4305.  
  4306. #if defined(IUCLC)
  4307.     tty.c_iflag &= ~IUCLC;        /* absent from POSIX */
  4308. #endif /* defined(IUCLC) */
  4309.  
  4310.     tty.c_lflag &= ~(ECHO | ICANON);
  4311.     tty.c_cc[4] = 5;        /* MIN */
  4312.     tty.c_cc[5] = 2;        /* TIME */
  4313.     (void)ioctl((int)(fileno(fptty)),(int)TCSETAF,(char*)&tty);
  4314.     }
  4315. }
  4316. #endif /* HAVE_TERMIO_H */
  4317.  
  4318.  
  4319. #if defined(HAVE_TERMIOS_H)
  4320. #undef HAVE_SGTTY_H
  4321. #undef HAVE_TERMIO_H
  4322. #include <termios.h>
  4323. static struct termios tty_save;    /* XPG3, POSIX.1, FIPS 151-1 interface */
  4324.  
  4325. static void
  4326. reset_terminal(VOID)                /* restore saved modes */
  4327. {
  4328.     if (tty_init == YES)
  4329.     (void)tcsetattr((int)(fileno(fptty)),TCSANOW,&tty_save);
  4330. }
  4331.  
  4332.  
  4333. static void
  4334. set_terminal(VOID)                /* set to cbreak input mode */
  4335. {
  4336.     struct termios tty;        /* XPG3, POSIX.1, FIPS 151-1 interface */
  4337.  
  4338.     if (tcgetattr((int)(fileno(fptty)),&tty) != -1)
  4339.     {
  4340.     tty_save = tty;
  4341.     tty_init = YES;
  4342.     tty.c_iflag &= ~(INLCR | ICRNL | ISTRIP | IXON | BRKINT);
  4343.  
  4344. #if defined(IUCLC)
  4345.     tty.c_iflag &= ~IUCLC;        /* absent from POSIX */
  4346. #endif /* defined(IUCLC) */
  4347.  
  4348.     tty.c_lflag &= ~(ECHO | ICANON);
  4349.     tty.c_cc[VMIN] = 5;        /* MIN */
  4350.     tty.c_cc[VTIME] = 2;        /* TIME */
  4351.     (void)tcsetattr((int)(fileno(fptty)),TCSANOW,&tty);
  4352.     }
  4353. }
  4354. #endif /* defined(HAVE_TERMIOS_H) */
  4355.  
  4356.  
  4357. static int
  4358. get_screen_lines(VOID)    /* this must come after terminal header includes! */
  4359. {
  4360. #if defined(TIOCGWINSZ)
  4361.     struct winsize window_size;
  4362.  
  4363.     if (fptty != (FILE*)NULL)
  4364.     {
  4365.     (void)ioctl((int)(fileno(fptty)),(int)TIOCGWINSZ,&window_size);
  4366.     if (window_size.ws_row > 0)
  4367.         return ((int)window_size.ws_row);
  4368.     }
  4369. #else /* defined(TIOCGWINSZ) */
  4370.     /* some systems store screen lines in environment variables  */
  4371.     char *p;
  4372.     int n;
  4373.  
  4374.     if (((p = getenv("ROWS")) != (char*)NULL) ||
  4375.     ((p = getenv("LINES")) != (char*)NULL))
  4376.     {
  4377.     n = (int)atoi(p);
  4378.     if (n > 0)
  4379.         return (n);
  4380.     }
  4381. #endif /* defined(TIOCGWINSZ) */
  4382.  
  4383.     return (SCREEN_LINES);
  4384. }
  4385. #endif /* (OS_UNIX && (SCREEN_LINES > 0)) */
  4386.  
  4387.  
  4388. #if (OS_VAXVMS && (SCREEN_LINES > 0))
  4389. #include <ssdef.h>
  4390. #include <descrip.h>
  4391. #include <iodef.h>
  4392. #include <ttdef.h>
  4393. #include <tt2def.h>
  4394.  
  4395. #define TTYOPENFLAGS "rb"
  4396. #define TTYNAME ctermid((char*)NULL)
  4397. static int status;            /* system service status */
  4398. static int tt_channel = -1;        /* terminal channel for image QIO's */
  4399. static int iomask;            /* QIO flag mask */
  4400. static $DESCRIPTOR(sys_in,"TT:");    /* terminal descriptor */
  4401.  
  4402. static struct
  4403. {
  4404.     unsigned char class;
  4405.     unsigned char type;
  4406.     unsigned short buffer_size;
  4407.     unsigned long tt;
  4408.     unsigned long tt2;
  4409. } mode_buf,mode_save;
  4410.  
  4411. #define FAILED(status) (~(status) & 1)    /* failure if LSB is 0 */
  4412.  
  4413. static int
  4414. get_screen_lines(VOID)
  4415. {
  4416.     short flags;
  4417.     short dvtype;
  4418.     short ncols;
  4419.     short nrows = 0;
  4420.  
  4421.     (void)lib$screen_info(&flags,&dvtype,&ncols,&nrows);
  4422.     return ((int)((nrows > 0) ? nrows : SCREEN_LINES));
  4423. }
  4424.  
  4425.  
  4426. static void
  4427. kbclose(VOID)
  4428. {
  4429.     (void)sys$qiow(0,tt_channel,IO$_SETMODE,0,0,0, &mode_save,12,0,0,0,0);
  4430. }
  4431.  
  4432.  
  4433. static KEYCODE
  4434. kbcode(VOID)
  4435. {
  4436.     int c = kbget();
  4437.  
  4438.     return ((c == EOF) ? KEY_EOF : keymap[(unsigned)c]);
  4439. }
  4440.  
  4441.  
  4442. static int
  4443. kbget(VOID)
  4444. {
  4445.     int c;
  4446.  
  4447.     status = sys$qiow(0,tt_channel,iomask,0,0,0,&c,1,0,0,0,0);
  4448.     return ((int)(FAILED(status) ? EOF : (c & 0xff)));
  4449. }
  4450.  
  4451.  
  4452. static void
  4453. kbopen(VOID)
  4454. {
  4455.     kbinitmap();
  4456.     status = sys$assign(&sys_in,&tt_channel,0,0);
  4457.     if (!FAILED(status))
  4458.     {
  4459.     (void)sys$qiow(0,tt_channel,IO$_SENSEMODE,0,0,0,&mode_save,12,0,0,0,0);
  4460.     mode_buf = mode_save;
  4461.     mode_buf.tt &= ~TT$M_WRAP;
  4462.     (void)sys$qiow(0,tt_channel,IO$_SETMODE,0,0,0,&mode_buf,12,0,0,0,0);
  4463.     iomask = IO$_TTYREADALL | IO$M_NOECHO;
  4464.     }
  4465. }
  4466. #endif /* (OS_VAXVMS && (SCREEN_LINES > 0)) */
  4467.  
  4468.  
  4469. #if (SCREEN_LINES > 0)
  4470. static void
  4471. kbinitmap(VOID)
  4472. {
  4473.     (void)memset((void*)&keymap[0],(int)KEY_UNKNOWN,sizeof(keymap));
  4474.  
  4475.     keymap[(unsigned)'b']    = KEY_PGUP;
  4476.     keymap[(unsigned)'B']    = KEY_PGUP;
  4477.     keymap[(unsigned)META('V')]    = KEY_PGUP;    /* Emacs scroll-down */
  4478.  
  4479.     keymap[(unsigned)'d']    = KEY_DOWN;
  4480.     keymap[(unsigned)'D']    = KEY_DOWN;
  4481.     keymap[(unsigned)CTL('N')]    = KEY_DOWN;    /* Emacs next-line*/
  4482.  
  4483.     keymap[(unsigned)'e']    = KEY_END;
  4484.     keymap[(unsigned)'E']    = KEY_END;
  4485.     keymap[(unsigned)META('>')]    = KEY_HOME;    /* Emacs end-of-buffer */
  4486.  
  4487.     keymap[(unsigned)'f']    = KEY_PGDN;
  4488.     keymap[(unsigned)'F']    = KEY_PGDN;
  4489.     keymap[(unsigned)' ']    = KEY_PGDN;
  4490.     keymap[(unsigned)'\r']    = KEY_PGDN;
  4491.     keymap[(unsigned)'\n']    = KEY_PGDN;
  4492.     keymap[(unsigned)CTL('V')]    = KEY_PGDN;    /* Emacs scroll-up */
  4493.  
  4494.     keymap[(unsigned)'h']    = KEY_HELP;
  4495.     keymap[(unsigned)'H']    = KEY_HELP;
  4496.     keymap[(unsigned)'?']    = KEY_HELP;
  4497.     keymap[(unsigned)CTL('H')]    = KEY_HELP;    /* Emacs help */
  4498.  
  4499.     keymap[(unsigned)'\033']    = KEY_QUIT;    /* ESCape gets out */
  4500.     keymap[(unsigned)'q']    = KEY_QUIT;
  4501.     keymap[(unsigned)'Q']    = KEY_QUIT;
  4502.  
  4503.     keymap[(unsigned)'.']    = KEY_AGAIN;
  4504.     keymap[(unsigned)'r']    = KEY_AGAIN;
  4505.     keymap[(unsigned)'R']    = KEY_AGAIN;
  4506.     keymap[(unsigned)CTL('L')]    = KEY_AGAIN;    /* Emacs recenter */
  4507.  
  4508.     keymap[(unsigned)'t']    = KEY_HOME;
  4509.     keymap[(unsigned)'T']    = KEY_HOME;
  4510.     keymap[(unsigned)META('<')]    = KEY_HOME;    /* Emacs beginning-of-buffer */
  4511.  
  4512.     keymap[(unsigned)'u']    = KEY_UP;
  4513.     keymap[(unsigned)'U']    = KEY_UP;
  4514.     keymap[(unsigned)CTL('P')]    = KEY_UP;     /* Emacs previous-line */
  4515. }
  4516. #endif /* (SCREEN_LINES > 0) */
  4517.  
  4518.  
  4519. #if NEW_STYLE
  4520. int
  4521. main(int argc, char *argv[])
  4522. #else /* K&R style */
  4523. int
  4524. main(argc,argv)
  4525. int argc;
  4526. char *argv[];
  4527. #endif /* NEW_STYLE */
  4528. {
  4529.  
  4530. #if defined(vms)
  4531.     extern char **cmd_lin();
  4532.  
  4533.     argv = cmd_lin( "", &argc, argv );
  4534. #endif /* defined(vms) */
  4535.  
  4536.     stdlog = stderr;    /* cannot assign at compile time on some systems */
  4537.  
  4538.     program_name = argv[0];
  4539.  
  4540.     check_inodes();
  4541.  
  4542. #if defined(DEBUG)
  4543.     fpdebug = tfopen("bibclean.dbg", "w");
  4544. #endif /* defined(DEBUG) */
  4545.  
  4546.     the_file.output.filename = "stdout";
  4547.  
  4548.     do_preargs(argc,argv);/* some args must be handled BEFORE initializations */
  4549.  
  4550.     if (read_initialization_files == YES)
  4551.     do_initfile(getenv(SYSPATH),INITFILE);
  4552.  
  4553.     if (read_initialization_files == YES)
  4554.     do_initfile(getenv(USERPATH),INITFILE);
  4555.  
  4556.     do_args(argc,argv);
  4557.  
  4558.     do_files(argc,argv);
  4559.  
  4560. #if OS_VAXVMS
  4561.     exit (error_count ? EXIT_FAILURE : EXIT_SUCCESS);
  4562. #endif /* OS_VAXVMS */
  4563.  
  4564.     return (error_count ? EXIT_FAILURE : EXIT_SUCCESS);
  4565. }
  4566.  
  4567.  
  4568. #if NEW_STYLE
  4569. static void
  4570. memmove(void *target, const void *source, size_t n)
  4571. #else /* K&R style */
  4572. static void
  4573. memmove(target, source, n)
  4574. void *target;
  4575. const void *source;
  4576. size_t n;
  4577. #endif /* NEW_STYLE */
  4578. {
  4579.     char *t;
  4580.     const char *s;
  4581.  
  4582.     t = (char *)target;
  4583.     s = (const char*)source;
  4584.     if ((s <= t) && (t < (s + n))) /* overlap: need right to left copy */
  4585.     {
  4586.     for (t = ((char *)target) + n - 1, s = ((const char*)source) + n - 1;
  4587.          n > 0; --n)
  4588.         *t-- = *s--;
  4589.     }
  4590.     else /* left to right copy is okay */
  4591.     {
  4592.     for ( ; n > 0; --n)
  4593.         *t++ = *s++;
  4594.     }
  4595. }
  4596.  
  4597.  
  4598. #if (defined(BSD) || defined(__SUNCC__))
  4599. #if NEW_STYLE
  4600. void*
  4601. memset(void *target, int value, size_t n)
  4602. #else /* K&R style */
  4603. void*
  4604. memset(target, value, n)
  4605. void *target;
  4606. int value;
  4607. size_t n;
  4608. #endif /* NEW_STYLE */
  4609. {
  4610.     unsigned char *t = (unsigned char*)target;
  4611.  
  4612.     for ( ; n > 0; --n)
  4613.     *t++ = (unsigned char)value;
  4614.  
  4615.     return (target);
  4616. }
  4617. #endif /* (defined(BSD) || defined(__SUNCC__)) */
  4618.  
  4619.  
  4620. static void
  4621. new_entry(VOID)            /* initialize for new BibTeX @name{...} */
  4622. {
  4623.     at_level = 0;
  4624.     brace_level = 0;
  4625.     rflag = NO;                /* already synchronized */
  4626. }
  4627.  
  4628.  
  4629. #if NEW_STYLE
  4630. static void
  4631. new_io_pair(IO_PAIR *pair)
  4632. #else /* K&R style */
  4633. static void
  4634. new_io_pair(pair)
  4635. IO_PAIR *pair;
  4636. #endif /* NEW_STYLE */
  4637. {
  4638.     new_position(&pair->input);
  4639.     new_position(&pair->output);
  4640. }
  4641.  
  4642.  
  4643. #if NEW_STYLE
  4644. static void
  4645. new_position(POSITION *position)
  4646. #else /* K&R style */
  4647. static void
  4648. new_position(position)
  4649. POSITION *position;
  4650. #endif /* NEW_STYLE */
  4651. {
  4652.     position->byte_position = 0L;
  4653.     position->last_column_position = 0L;
  4654.     position->column_position = 0L;
  4655.     position->line_number = 1L;
  4656. }
  4657.  
  4658.  
  4659. static void
  4660. opt_author(VOID)
  4661. {
  4662.     static CONST char *author[] =
  4663.     {
  4664.     "Author:\n",
  4665.     "\tNelson H. F. Beebe\n",
  4666.     "\tCenter for Scientific Computing\n",
  4667.     "\tDepartment of Mathematics\n",
  4668.     "\tUniversity of Utah\n",
  4669.     "\tSalt Lake City, UT 84112\n",
  4670.     "\tUSA\n",
  4671.     "\tTel: +1 801 581 5254\n",
  4672.     "\tFAX: +1 801 581 4801\n",
  4673.     "\tEmail: <beebe@math.utah.edu>\n",
  4674.     (const char*)NULL,
  4675.     };
  4676.  
  4677.     out_lines(stdlog, author, NO);
  4678. }
  4679.  
  4680.  
  4681. static void
  4682. opt_check_values(VOID)
  4683. {
  4684.     check_values = YESorNOarg();
  4685. }
  4686.  
  4687.  
  4688. static void
  4689. opt_delete_empty_fields(VOID)
  4690. {
  4691.     delete_empty_fields = YESorNOarg();
  4692. }
  4693.  
  4694.  
  4695. static void
  4696. opt_error_log(VOID)
  4697. {
  4698.     current_index++;
  4699.     if ((stdlog = tfopen(next_option,"w")) == (FILE*)NULL)
  4700.     {
  4701.         fprintf(stderr, "%s cannot open error log file [%s]",
  4702.             WARNING_PREFIX, next_option);
  4703.         fprintf(stderr, " -- using stderr instead\n");
  4704.         stdlog = stderr;
  4705.     }
  4706.     else
  4707.         check_inodes();                /* stdlog changed */
  4708. }
  4709.  
  4710.  
  4711. static void
  4712. opt_file_position(VOID)
  4713. {
  4714.     show_file_position = YESorNOarg();
  4715. }
  4716.  
  4717.  
  4718. static void
  4719. opt_fix_initials(VOID)
  4720. {
  4721.     fix_initials = YESorNOarg();
  4722. }
  4723.  
  4724.  
  4725. static void
  4726. opt_fix_names(VOID)
  4727. {
  4728.     fix_names = YESorNOarg();
  4729. }
  4730.  
  4731.  
  4732. static void
  4733. opt_help(VOID)
  4734. {
  4735.     static CONST char *help_lines[] =
  4736.     {
  4737.     "\nUsage: ",
  4738.     (const char*)NULL,
  4739.     " [ -author ] [ -error-log filename ] [ -help ] [ '-?' ]\n",
  4740.     "\t[ -init-file filename ] [ -[no-]check-values ]\n",
  4741.     "\t[ -[no-]delete-empty-fields ] [ -[no-]file-position ]\n",
  4742.     "\t[ -[no-]fix-initials ] [ -[no-]fix-names ]\n",
  4743.     "\t[ -[no-]par-breaks ] [ -[no-]print-patterns ]\n",
  4744.     "\t[ -[no-]read-init-files ] [ -[no-]remove-OPT-prefixes ]\n",
  4745.     "\t[ -[no-]scribe ] [ -[no-]trace-file-opening ] [ -[no-]warnings ]\n",
  4746.     "\t[ -version ]\n",
  4747.     "\t[ <infile or bibfile1 bibfile2 bibfile3 ...] >outfile\n",
  4748.     "\n",
  4749. #include "bibclean.h"
  4750.     };
  4751.  
  4752.     help_lines[1] = program_name;    /* cannot have this in initializer */
  4753.     out_lines(stdlog, help_lines, (screen_lines > 0) ? YES : NO);
  4754.     exit(EXIT_SUCCESS);
  4755. }
  4756.  
  4757.  
  4758. static void
  4759. opt_init_file(VOID)
  4760. {
  4761.     current_index++;
  4762.     do_initfile((const char*)NULL,next_option);
  4763. }
  4764.  
  4765.  
  4766. static void
  4767. opt_parbreaks(VOID)
  4768. {
  4769.     parbreaks = YESorNOarg();
  4770. }
  4771.  
  4772.  
  4773. static void
  4774. opt_print_patterns(VOID)
  4775. {
  4776.     print_patterns = YESorNOarg();
  4777. }
  4778.  
  4779.  
  4780. static void
  4781. opt_read_init_files(VOID)
  4782. {
  4783.     read_initialization_files = YESorNOarg();
  4784. }
  4785.  
  4786.  
  4787. static void
  4788. opt_remove_OPT_prefixes(VOID)
  4789. {
  4790.     remove_OPT_prefixes = YESorNOarg();
  4791. }
  4792.  
  4793.  
  4794. static void
  4795. opt_scribe(VOID)
  4796. {
  4797.     Scribe = YESorNOarg();
  4798. }
  4799.  
  4800.  
  4801. static void
  4802. opt_trace_file_opening(VOID)
  4803. {
  4804.     trace_file_opening = YESorNOarg();
  4805. }
  4806.  
  4807.  
  4808. static void
  4809. opt_version(VOID)
  4810. {
  4811.     version();
  4812. }
  4813.  
  4814.  
  4815. static void
  4816. opt_warnings(VOID)
  4817. {
  4818.     warnings = YESorNOarg();
  4819. }
  4820.  
  4821.  
  4822. static void
  4823. out_equals(VOID)
  4824. {
  4825.     out_c(' ');
  4826.     out_c('=');            /* standardize to = */
  4827.     out_c(' ');            /* always surround = by spaces */
  4828. }
  4829.  
  4830.  
  4831. #if NEW_STYLE
  4832. static void
  4833. out_error(FILE *fpout, const char *s)
  4834. #else /* K&R style */
  4835. static void
  4836. out_error(fpout, s)
  4837. FILE *fpout;
  4838. const char *s;
  4839. #endif /* NEW_STYLE */
  4840. {
  4841.     if (fpout == stdout)    /* private handling of stdout so we */
  4842.     out_s(s);        /* can track positions */
  4843.     else
  4844.     (void)fputs(s,fpout);
  4845. }
  4846.  
  4847.  
  4848. static void
  4849. out_flush(VOID)            /* flush buffered output */
  4850. {
  4851.     out_c(EOF);            /* magic value to flush buffers */
  4852. }
  4853.  
  4854.  
  4855. static void
  4856. out_key(VOID)
  4857. {
  4858.     out_spaces(KEY_INDENTATION);
  4859.     out_s(current_key);
  4860. }
  4861.  
  4862.  
  4863. #if NEW_STYLE
  4864. static void
  4865. out_lines(FILE *fpout, const char *lines[], YESorNO pause)
  4866. #else /* K&R style */
  4867. static void
  4868. out_lines(fpout, lines, pause)
  4869. FILE *fpout;
  4870. const char *lines[];
  4871. YESorNO pause;
  4872. #endif /* NEW_STYLE */
  4873. {
  4874.     int k;
  4875.  
  4876. #if (SCREEN_LINES > 0)
  4877.     int lines_on_screen;
  4878.     int nlines;
  4879.  
  4880.     if (pause == YES)
  4881.     {
  4882.     kbopen();
  4883.     for (nlines = 0; lines[nlines] != (const char*)NULL; ++nlines)
  4884.         NOOP;                /* count number of lines */
  4885.  
  4886.     for (k = 0, lines_on_screen = 0; ; )
  4887.     {
  4888.         if (lines[k] != (const char*)NULL)
  4889.         {
  4890.         (void)fputs(lines[k], fpout);
  4891.         if (strchr(lines[k],'\n') != (char*)NULL)
  4892.             lines_on_screen++;    /* some lines[k] are only partial */
  4893.         }
  4894.  
  4895.         if ((lines_on_screen == (screen_lines - 2)) ||
  4896.            (lines[k] == (const char*)NULL))
  4897.         {                    /* pause for user action */
  4898.         lines_on_screen = 0;
  4899.         screen_lines = get_screen_lines(); /* maybe window resize? */
  4900.         k = do_more(fpout,k,screen_lines - 2);
  4901.         if (k == EOF)
  4902.             break;            /* here's the loop exit */
  4903.         else if (k == LAST_SCREEN_LINE)
  4904.             k = nlines - (screen_lines - 2);
  4905.         if (k < 0)            /* ensure k stays in range */
  4906.             k = 0;
  4907.         else if (k >= nlines)
  4908.             k = nlines - 1;
  4909.         }
  4910.         else            /* still filling current screen */
  4911.         k++;
  4912.     }                    /* end for (k...) */
  4913.     kbclose();
  4914.     }
  4915.     else                    /* pause == NO */
  4916.     {
  4917.     for (k = 0; lines[k] != (const char*)NULL; k++)
  4918.         (void)fputs(lines[k], fpout);
  4919.     }
  4920. #else /* NOT (SCREEN_LINES > 0) */
  4921.     for (k = 0; lines[k] != (const char*)NULL; k++)
  4922.     (void)fputs(lines[k], fpout);
  4923. #endif /* (SCREEN_LINES > 0) */
  4924.  
  4925. }
  4926.  
  4927.  
  4928. #if NEW_STYLE
  4929. static void
  4930. out_position(FILE* fpout, const char *msg, IO_PAIR *the_location)
  4931. #else /* K&R style */
  4932. static void
  4933. out_position(fpout,msg,the_location)
  4934. FILE* fpout;
  4935. const char *msg;
  4936. IO_PAIR *the_location;
  4937. #endif /* NEW_STYLE */
  4938. {
  4939.     char s[sizeof(
  4940.     " output byte=XXXXXXXXXX line=XXXXXXXXXX column=XXXXXXXXXX")+1];
  4941.  
  4942.     out_error(fpout, msg);
  4943.     (void)sprintf(s," input byte=%ld line=%ld column=%2ld",
  4944.           the_location->input.byte_position,
  4945.           the_location->input.line_number,
  4946.           the_location->input.column_position);
  4947.     out_error(fpout, s);
  4948.  
  4949.     (void)sprintf(s, " output byte=%ld line=%ld column=%2ld\n",
  4950.           the_location->output.byte_position,
  4951.           the_location->output.line_number,
  4952.           the_location->output.column_position);
  4953.     out_error(fpout, s);
  4954. }
  4955.  
  4956.  
  4957. #if NEW_STYLE
  4958. static void
  4959. out_s(const char *s)        /* output a string, wrapping long lines */
  4960. #else /* K&R style */
  4961. static void
  4962. out_s(s)            /* output a string, wrapping long lines */
  4963. const char *s;
  4964. #endif /* NEW_STYLE */
  4965. {
  4966.     /* The strings s[] has already had runs of whitespace of all kinds
  4967.        collapsed to single spaces.  The word_length() function returns 1
  4968.        more than the actual non-blank word length at end of string, so
  4969.        that we can automatically account for the comma that will be
  4970.        supplied after the string. */
  4971.  
  4972.     for (; *s; ++s)
  4973.     {
  4974.     switch (*s)
  4975.     {
  4976.     case ' ':        /* may change space to new line and indent */
  4977.         if ((the_file.output.column_position + 1 + word_length(s+1))
  4978.         > MAX_COLUMN)
  4979.         wrap_line();
  4980.         else
  4981.         out_c(*s);
  4982.         break;
  4983.  
  4984.     case '!':        /* may wrap after certain punctuation */
  4985.     case '&':
  4986.     case '+':
  4987.     case ',':
  4988.     case '.':
  4989.     case ':':
  4990.     case ';':
  4991.     case '=':
  4992.     case '?':
  4993.         out_c(*s);
  4994.         if ((the_file.output.column_position + word_length(s+1))
  4995.         > MAX_COLUMN)
  4996.         wrap_line();
  4997.         break;
  4998.  
  4999.     default:        /* everything else is output verbatim */
  5000.         out_c(*s);
  5001.     }
  5002.     }
  5003. }
  5004.  
  5005.  
  5006. #if NEW_STYLE
  5007. static void
  5008. out_spaces(int n)
  5009. #else /* K&R style */
  5010. static void
  5011. out_spaces(n)
  5012. int n;
  5013. #endif /* NEW_STYLE */
  5014. {
  5015.     for (; n > 0; --n)
  5016.     out_c(' ');
  5017. }
  5018.  
  5019.  
  5020. #if NEW_STYLE
  5021. static void
  5022. out_status (FILE* fpout,const char *prefix)
  5023. #else /* K&R style */
  5024. static void
  5025. out_status(fpout,prefix)
  5026. FILE* fpout;
  5027. const char *prefix;
  5028. #endif /* NEW_STYLE */
  5029. {
  5030.     if (show_file_position == YES)
  5031.     {
  5032.     out_error(fpout, prefix);
  5033.     out_error(fpout, "  File positions:  input [");
  5034.     out_error(fpout, the_file.input.filename);
  5035.     out_error(fpout, "]  output [");
  5036.     out_error(fpout, the_file.output.filename);
  5037.     out_error(fpout, "]\n");
  5038.  
  5039.     out_error(fpout, prefix);
  5040.     out_position(fpout, "  Entry  ", &the_entry);
  5041.  
  5042.     out_error(fpout, prefix);
  5043.     out_position(fpout, "  Value  ", &the_value);
  5044.  
  5045.     out_error(fpout, prefix);
  5046.     out_position(fpout, "  Current", &the_file);
  5047.     }
  5048. }
  5049.  
  5050.  
  5051. static void
  5052. out_value(VOID)
  5053. {
  5054.     static KEY_FUNCTION_ENTRY checks[] =
  5055.     {
  5056.         {"author",        6,    check_other},
  5057.         {"chapter",        7,    check_chapter},
  5058.     {"ISBN",        4,    check_ISBN},
  5059.     {"ISSN",        4,    check_ISSN},
  5060.         {"month",        5,    check_month},
  5061.         {"number",        6,    check_number},
  5062.         {"pages",        5,    check_pages},
  5063.         {"volume",        6,    check_volume},
  5064.         {"year",        4,    check_year},
  5065.     {(const char*)NULL,    0,    (void (*)(VOID))NULL},
  5066.     };
  5067.  
  5068.     static KEY_FUNCTION_ENTRY fixes[] =
  5069.     {
  5070.     {"author",        6,    fix_namelist},
  5071.         {"editor",        6,    fix_namelist},
  5072.     {"month",        5,    fix_month},
  5073.     {"pages",        5,    fix_pages},
  5074.     {"title",        5,    fix_title},
  5075.     {(const char*)NULL,    0,    (void (*)(VOID))NULL},
  5076.     };
  5077.  
  5078.     trim_value();
  5079.  
  5080.     (void)apply_function(current_key,fixes);
  5081.  
  5082.     if (check_values == YES)
  5083.     {
  5084.     if (apply_function(current_key,checks) == NO)
  5085.         check_other();
  5086.     }
  5087.  
  5088.     if ((remove_OPT_prefixes == YES) &&
  5089.     (strncmp(current_key,"OPT",3) == 0) &&
  5090.     (strlen(current_key) > 3) &&
  5091.     (strlen(current_value) > 2)) /* 2, not 0: quotes are included! */
  5092.     {
  5093.     out_c(DELETE_LINE);
  5094.     memmove(current_key,¤t_key[3],(size_t)(strlen(current_key)-3+1));
  5095.                     /* reduce "OPTname" to "name" */
  5096.     out_key();
  5097.     out_equals();
  5098.     out_spaces((int)(VALUE_INDENTATION - the_file.output.column_position));
  5099.     }
  5100.     else if ((delete_empty_fields == YES) && (strlen(current_value) <= 2))
  5101.     {                /* 2, not 0, because quotes are included! */
  5102.     out_c(DELETE_LINE);
  5103.     discard_next_comma = YES;
  5104.     return;
  5105.     }
  5106.     out_s(current_value);
  5107.     check_length(strlen(current_value));
  5108. }
  5109.  
  5110.  
  5111. #if NEW_STYLE
  5112. static void
  5113. out_with_error(const char *s, const char *msg)
  5114. #else /* K&R style */
  5115. static void
  5116. out_with_error(s,msg)    /* output string s, error message, and resynchronize */
  5117. const char *s;
  5118. const char *msg;
  5119. #endif /* NEW_STYLE */
  5120. {
  5121.     out_s(s);
  5122.     error(msg);
  5123.     resync();
  5124. }
  5125.  
  5126.  
  5127. #if NEW_STYLE
  5128. static void
  5129. out_with_parbreak_error(char *s)
  5130. #else /* K&R style */
  5131. static void
  5132. out_with_parbreak_error(s)
  5133. char *s;
  5134. #endif /* NEW_STYLE */
  5135. {
  5136.     out_with_error(s, "Unexpected paragraph break for key ``%k''");
  5137. }
  5138.  
  5139.  
  5140. #if NEW_STYLE
  5141. static void
  5142. prt_pattern(const char *keyname, const char *pattern, const char *message)
  5143. #else /* K&R style */
  5144. static void
  5145. prt_pattern(keyname,pattern,message)
  5146. const char *keyname;
  5147. const char *pattern;
  5148. const char *message;
  5149. #endif /* NEW_STYLE */
  5150. {
  5151.     if (print_patterns == YES)
  5152.     {
  5153.     if ((pattern == (const char*)NULL) || (*pattern == '\0'))
  5154.         (void)fprintf(stdlog,
  5155.         "\nfile=[%s] key=[%-12s] existing patterns discarded\n\n",
  5156.         initialization_file_name, keyname);
  5157.     else if (message == (char*)NULL)
  5158.         (void)fprintf(stdlog,
  5159.         "file=[%s] key=[%-12s] pattern=[%s]\n",
  5160.         initialization_file_name, keyname, pattern);
  5161.     else
  5162.         (void)fprintf(stdlog,
  5163.         "file=[%s] key=[%-12s] pattern=[%s] message[%s]\n",
  5164.         initialization_file_name, keyname, pattern, message);
  5165.     }
  5166. }
  5167.  
  5168.  
  5169. #if NEW_STYLE
  5170. static void
  5171. put_back(int c)        /* put last get_char() value back onto input stream */
  5172. #else /* K&R style */
  5173. static void
  5174. put_back(c)        /* put last get_char() value back onto input stream */
  5175. int c;
  5176. #endif /* NEW_STYLE */
  5177. {
  5178.     ungetc(c,fpin);
  5179.     the_file.input.byte_position--;
  5180.  
  5181.     /* Adjust status values that are set in get_char() */
  5182.  
  5183.     if (!isspace(c))
  5184.     non_white_chars--;
  5185.  
  5186.     if (c == EOF)
  5187.     eofile = NO;
  5188.     else if (c == '\n')
  5189.     {
  5190.     the_file.input.column_position = the_file.input.last_column_position;
  5191.     the_file.input.line_number--;
  5192.     }
  5193.     else if (c == '\t')
  5194.     the_file.input.column_position = the_file.input.last_column_position;
  5195.     else
  5196.     the_file.input.column_position--;
  5197.     if (c == '{')
  5198.     brace_level--;
  5199.     else if (c == '}')
  5200.     brace_level++;
  5201. }
  5202.  
  5203.  
  5204. #if NEW_STYLE
  5205. static void
  5206. put_back_string(const char *s)    /* put string value back onto input stream */
  5207. #else /* K&R style */
  5208. static void
  5209. put_back_string(s)    /* put string value back onto input stream */
  5210. const char *s;
  5211. #endif /* NEW_STYLE */
  5212. {
  5213.     char *p;
  5214.  
  5215.     for (p = strchr(s,'\0') - 1; p >= s; p--)
  5216.     put_back(*p);
  5217. }
  5218.  
  5219.  
  5220. #if NEW_STYLE
  5221. static void            /* output c, but trim trailing blanks, */
  5222. put_char(int c)            /* and output buffer if c == EOF */
  5223. #else /* K&R style */
  5224. static void
  5225. put_char(c)            /* output c, but trim trailing blanks, */
  5226. int c;                /* and output buffer if c == EOF */
  5227. #endif /* NEW_STYLE */
  5228. {
  5229.     static int buf_length = 0;
  5230.     static char buf[MAX_BUFFER+1]; /* 1 extra slot for trailing NUL */
  5231.  
  5232.     the_file.output.byte_position++;
  5233.     if ((c == EOF) || (buf_length >= MAX_BUFFER))
  5234.     {
  5235.     buf[buf_length] = (char)'\0';
  5236.     if (buf_length > 0)
  5237.     {
  5238.         (void)fputs(buf,stdout);
  5239.         (void)fflush(stdout);
  5240.         buf_length = 0;
  5241.     }
  5242.     if (c == EOF)
  5243.         return;
  5244.     }
  5245.     switch (c)
  5246.     {
  5247.     case '\n':            /* trim trailing spaces */
  5248.     the_file.output.line_number++;
  5249.     the_file.output.column_position = 0L;
  5250.     while ((buf_length > 0) && (buf[buf_length-1] == ' '))
  5251.     {
  5252.         the_file.output.byte_position--;
  5253.         buf_length--;
  5254.     }
  5255.     the_file.input.last_column_position =
  5256.         the_file.input.column_position - 1;
  5257.                 /* inexact if we trimmed tabs. */
  5258.     break;
  5259.  
  5260.     case '\t':
  5261.     the_file.input.last_column_position = the_file.input.column_position;
  5262.     the_file.output.column_position =
  5263.         (the_file.output.column_position + 8L) & ~07L;
  5264.     break;
  5265.  
  5266.     case DELETE_CHAR:        /* delete a character from the output */
  5267.     if (buf_length <= 0)    /* this should NEVER happen! */
  5268.         fatal("Internal error: too many output characters deleted");
  5269.     if (buf[buf_length] == '\n')
  5270.         the_file.output.line_number--;
  5271.     the_file.output.column_position--; /* inexact if tab deleted */
  5272.     the_file.output.byte_position--;
  5273.     buf_length--;
  5274.     return;            /* don't store this character! */
  5275.  
  5276.     case DELETE_LINE:        /* delete back to beginning of line */
  5277.     while ((buf_length > 0) && (buf[buf_length-1] != '\n'))
  5278.     {
  5279.         buf_length--;
  5280.         the_file.output.byte_position--;
  5281.     }
  5282.     the_file.output.column_position = 0;
  5283.     return;            /* don't store this character! */
  5284.  
  5285.     default:
  5286.     the_file.input.last_column_position = the_file.input.column_position;
  5287.     the_file.output.column_position++;
  5288.     break;
  5289.     }                /* end switch (c) */
  5290.     buf[buf_length++] = (char)c;
  5291. }
  5292.  
  5293.  
  5294. static void
  5295. resync(VOID)            /* copy input to output until new entry met */
  5296. {                /* and set resynchronization flag */
  5297.     rflag = YES;
  5298.     do_other();            /* copy text until new entry found */
  5299. }
  5300.  
  5301.  
  5302. #if NEW_STYLE
  5303. char*
  5304. strdup(const char *s)
  5305. #else /* K&R style */
  5306. char*
  5307. strdup(s)
  5308. const char *s;
  5309. #endif /* NEW_STYLE */
  5310. {
  5311.     char *p;
  5312.     p = (char*)malloc(strlen(s)+1);
  5313.     if (p == (char*)NULL)
  5314.     fatal("Out of string memory");
  5315.     return (strcpy(p,s));
  5316. }
  5317.  
  5318.  
  5319. #if NEW_STYLE
  5320. int
  5321. strnicmp(const char *s1, const char *s2, size_t n)
  5322. #else /* K&R style */
  5323. int
  5324. strnicmp(s1,s2,n)
  5325. const char    *s1;
  5326. const char    *s2;
  5327. size_t        n;
  5328. #endif /* NEW_STYLE */
  5329. {
  5330.     int       c1;
  5331.     int       c2;
  5332.  
  5333.     /*******************************************************************
  5334.       Compare strings ignoring case, stopping after n characters, or at
  5335.       end-of-string, whichever comes first.
  5336.     *******************************************************************/
  5337.  
  5338.     for (; (n > 0) && *s1 && *s2; ++s1, ++s2, --n)
  5339.     {
  5340.     c1 = 0xff & (int)(islower(*s1) ? (int)*s1 : tolower(*s1));
  5341.     c2 = 0xff & (int)(islower(*s2) ? (int)*s2 : tolower(*s2));
  5342.     if (c1 < c2)
  5343.         return (-1);
  5344.     else if (c1 > c2)
  5345.         return (1);
  5346.     }
  5347.     if (n <= 0)           /* first n characters match */
  5348.     return (0);
  5349.     else if (*s1 == '\0')
  5350.     return ((*s2 == '\0') ? 0 : -1);
  5351.     else /* (*s2 == '\0') */
  5352.     return (1);
  5353. }
  5354.  
  5355.  
  5356. #if NEW_STYLE
  5357. static FILE*
  5358. tfopen(const char *filename, const char *mode) /* traced file opening */
  5359. #else /* K&R style */
  5360. static FILE*
  5361. tfopen(filename,mode)
  5362. const char *filename;
  5363. const char *mode;
  5364. #endif /* NEW_STYLE */
  5365. {
  5366.     FILE *fp;
  5367.  
  5368.     fp = FOPEN(filename,mode);
  5369.     if (trace_file_opening == YES)
  5370.     (void)fprintf(stdlog,"%s open file [%s]%s\n",
  5371.         WARNING_PREFIX, filename, (fp == (FILE*)NULL) ? ": FAILED" : "");
  5372.     return (fp);
  5373. }
  5374.  
  5375.  
  5376. static void
  5377. trim_value(VOID)
  5378. {        /* trim leading and trailing space from current_value[] */
  5379.     size_t k;
  5380.     size_t n = strlen(current_value);
  5381.  
  5382.     if ((current_value[0] == '"') && isspace(current_value[1]))
  5383.     {        /* then quoted string value with leading space*/
  5384.     for (k = 1; (k < n) && isspace(current_value[k]); ++k)
  5385.         NOOP;
  5386.     memmove(¤t_value[1], ¤t_value[k], (size_t)(n + 1 - k));
  5387.                 /* copy includes trailing NULL */
  5388.     n = strlen(current_value);
  5389.     }
  5390.     if (current_value[n-1] == '"')
  5391.     {
  5392.     for (k = n; (k > 1) && isspace(current_value[k-2]); --k)
  5393.         NOOP;
  5394.     current_value[k-1] = (char)'"';
  5395.     current_value[k] = (char)'\0';
  5396.     }
  5397. }
  5398.  
  5399.  
  5400. static void
  5401. unexpected(VOID)
  5402. {
  5403.     warning("Unexpected value in ``%k = %v''");
  5404. }
  5405.  
  5406.  
  5407. static void
  5408. usage(VOID)
  5409. {
  5410.     static CONST char *usage_lines[] =
  5411.     {
  5412.     "\nUsage: ",
  5413.     (const char*)NULL,
  5414.     " [ -author ] [ -error-log filename ] [ -help ] [ '-?' ]\n",
  5415.     "\t[ -init-file filename ] [ -[no-]check-values ]\n",
  5416.     "\t[ -[no-]delete-empty-fields ] [ -[no-]file-position ]\n",
  5417.     "\t[ -[no-]fix-initials ] [ -[no-]fix-names ]\n",
  5418.     "\t[ -[no-]par-breaks ] [ -[no-]print-patterns ]\n",
  5419.     "\t[ -[no-]read-init-files ] [ -[no-]remove-OPT-prefixes ]\n",
  5420.     "\t[ -[no-]scribe ] [ -[no-]trace-file-opening ] [ -[no-]warnings ]\n",
  5421.     "\t[ -version ]\n",
  5422.     "\t[ <infile or bibfile1 bibfile2 bibfile3 ...] >outfile\n",
  5423.     (const char*)NULL,
  5424.     };
  5425.  
  5426.     version();
  5427.     usage_lines[1] = program_name;    /* cannot have this in initializer */
  5428.     out_lines(stdlog, usage_lines, NO);
  5429. }
  5430.  
  5431.  
  5432. static void
  5433. version(VOID)
  5434. {
  5435.     static CONST char *version_string[] =
  5436.     {
  5437.     BIBCLEAN_VERSION,
  5438.     "\n",
  5439.  
  5440. #if defined(HOST) || defined(USER) || defined(__DATE__) || defined(__TIME__)
  5441.     "Compiled",
  5442.  
  5443. #if defined(USER)
  5444.     " by <", USER,
  5445.  
  5446. #if defined(HOST)
  5447.     "@", HOST,
  5448. #endif /* defined(HOST) */
  5449.  
  5450.     ">",
  5451. #endif /* defined(USER) */
  5452.  
  5453. #if defined(__DATE__)
  5454.     " on ", __DATE__,
  5455. #endif /* defined(__DATE__) */
  5456.  
  5457. #if defined(__TIME__)
  5458.     " ", __TIME__,
  5459. #endif /* defined(__TIME__) */
  5460.  
  5461. #if defined(HAVE_PATTERNS)
  5462.     "\nwith native pattern matching",
  5463. #endif /* defined(HAVE_PATTERNS) */
  5464.  
  5465. #if defined(HAVE_RECOMP) || defined(HAVE_REGEXP)
  5466.     "\nwith regular-expression pattern matching",
  5467. #endif /* defined(HAVE_RECOMP) || defined(HAVE_REGEXP) */
  5468.  
  5469. #if defined(HAVE_OLDCODE)
  5470.     "\nwith old matching code",
  5471. #endif /* defined(HAVE_OLDCODE) */
  5472.  
  5473.     "\n",
  5474. #endif /* defined(HOST)||defined(USER)||defined(__DATE__)||defined(__TIME__) */
  5475.  
  5476.     (const char*)NULL,
  5477.     };
  5478.  
  5479.     out_lines(stdlog, version_string, NO);
  5480. }
  5481.  
  5482.  
  5483. #if NEW_STYLE
  5484. static void
  5485. warning(const char *msg)    /* issue a warning message to stdlog */
  5486. #else /* K&R style */
  5487. static void
  5488. warning(msg)            /* issue a warning message to stdlog */
  5489. const char *msg;
  5490. #endif /* NEW_STYLE */
  5491. {
  5492.     if (warnings == YES)
  5493.     {
  5494.     out_flush();        /* flush all buffered output */
  5495.  
  5496.     /* Because warnings are often issued in the middle of lines, we
  5497.        start a new line if stdlog and stdout are the same file. */
  5498.  
  5499.     (void)fprintf(stdlog,"%s%s \"%s\", line %ld: %s.\n",
  5500.         (stdlog_on_stdout == YES) ? "\n" : "",
  5501.         WARNING_PREFIX, the_file.input.filename,
  5502.         the_value.input.line_number, format(msg));
  5503.     out_status(stdlog, WARNING_PREFIX);
  5504.     (void)fflush(stdlog);
  5505.     }
  5506. }
  5507.  
  5508.  
  5509. #if NEW_STYLE
  5510. static int
  5511. word_length(const char *s)    /* return length of leading non-blank prefix */
  5512. #else /* K&R style */
  5513. static int
  5514. word_length(s)            /* return length of leading non-blank prefix */
  5515. const char *s;
  5516. #endif /* NEW_STYLE */
  5517. {
  5518.     size_t n;
  5519.  
  5520.     for (n = 0; s[n]; ++n)
  5521.     {
  5522.     if (isspace(s[n]))
  5523.         break;
  5524.     }
  5525.     return ((int)((s[n] == '\0') ? n + 1 : n));
  5526.                 /* at end of string, return one more than */
  5527.                 /* true length to simplify line wrapping */
  5528. }
  5529.  
  5530.  
  5531. static void
  5532. wrap_line(VOID)            /* insert a new line and leading indentation */
  5533. {
  5534.     out_c('\n');
  5535.     out_spaces(VALUE_INDENTATION); /* supply leading indentation */
  5536. }
  5537.  
  5538.  
  5539. static YESorNO
  5540. YESorNOarg(VOID)
  5541. {
  5542.     return ((strnicmp(current_option+1,"no-",3) == 0) ? NO : YES);
  5543. }
  5544.  
  5545.  
  5546. /***********************************************************************
  5547.  We put this regular expression matching code last because
  5548.    (a) it is not universally available,
  5549.    (b) the 6 macros in the HAVE_REGEXP section can only be defined
  5550.        once, and
  5551.    (c) there are three variants: the old ugly regexp.h interface (HAVE_REGEXP),
  5552.        the new clean regex.h interface (HAVE_RECOMP), and the GNU version
  5553.        (not yet supported here)
  5554. ***********************************************************************/
  5555.  
  5556. /**********************************************************************/
  5557.  
  5558. #if defined(HAVE_RECOMP)
  5559. #if (_AIX || ultrix)
  5560. /* AIX 370, AIX PS/2, and ULTRIX have these, but no regex.h, sigh... */
  5561. #if __cplusplus
  5562. extern "C" {
  5563. #endif /* __cplusplus */
  5564.  
  5565. char        *re_comp ARGS((const char *s_));
  5566. int        re_exec ARGS((const char *s_));
  5567.  
  5568. #if __cplusplus
  5569. };
  5570. #endif /* __cplusplus */
  5571.  
  5572. #else /* NOT (_AIX || ultrix) */
  5573. #include <regex.h>
  5574. #endif /* (_AIX || ultrix) */
  5575.  
  5576. #if NEW_STYLE
  5577. static int
  5578. match_regexp(const char *string,const char *pattern)
  5579. #else /* K&R style */
  5580. static int
  5581. match_regexp(string,pattern)
  5582. const char *string;
  5583. const char *pattern;
  5584. #endif /* NEW_STYLE */
  5585. {
  5586.     if (re_comp(pattern) != (char*)NULL)
  5587.     fatal("Internal error: bad regular expression");
  5588.     switch (re_exec(string))
  5589.     {
  5590.     case 1:
  5591.     return (YES);
  5592.     case 0:
  5593.     return (NO);
  5594.     default:
  5595.     fatal("Internal error: bad regular expression");
  5596.     }
  5597.     return (YES);        /* keep optimizers happy */
  5598. }
  5599. #endif /* defined(HAVE_RECOMP) */
  5600.  
  5601. /**********************************************************************/
  5602.  
  5603. #if defined(HAVE_REGEXP)
  5604. const char        *sp_global;
  5605. #define ERROR(c)    regerr()
  5606. #define GETC()        (*sp++)
  5607. #define INIT           const char *sp = sp_global;
  5608. #define PEEKC()        (*sp)
  5609. #define RETURN(c)    return(c)
  5610. #define UNGETC(c)    (--sp)
  5611.  
  5612. void
  5613. regerr(VOID)
  5614. {
  5615.     fatal("Internal error: bad regular expression");
  5616. }
  5617.  
  5618. #include <regexp.h>
  5619.  
  5620. #if NEW_STYLE
  5621. static int
  5622. match_regexp(const char *string,const char *pattern)
  5623. #else /* K&R style */
  5624. static int
  5625. match_regexp(string,pattern)
  5626. const char *string;
  5627. const char *pattern;
  5628. #endif /* NEW_STYLE */
  5629. {
  5630.     char    expbuf[MAX_TOKEN_SIZE];
  5631.  
  5632.     sp_global = string;
  5633.     (void)compile((char*)pattern, (char*)expbuf,
  5634.     (char*)(expbuf + sizeof(expbuf)), '\0');
  5635.     return (step((char*)string,(char*)expbuf) ? YES : NO);
  5636. }
  5637. #endif /* defined(HAVE_REGEXP) */
  5638. /**********************************************************************/
  5639.